Rational and VFP Numbers: Difference between revisions
(Created page with "===Introduction=== <p><b>Rational numbers</b> complement the 64-bit integer datatype to provide infinite precision (or <apll>WS FULL</apll>) at the cost of some performance. Si...") |
No edit summary |
||
Line 7: | Line 7: | ||
===Precision=== | ===Precision=== | ||
<p>Rational numbers have infinite precision. They are stored with a separate numerator and denominator, both of which are exact numbers in the sense that their size (and hence precision) may grow limited only by the available workspace. Rational numbers complement the 64-bit integer datatype to provide infinite precision (or <apll>WS FULL</apll>) at the cost of some performance.</p> | <p>Rational numbers have <b>infinite precision</b>. They are stored with a separate numerator and denominator, both of which are exact numbers in the sense that their size (and hence precision) may grow limited only by the available workspace. Rational numbers complement the 64-bit integer datatype to provide infinite precision (or <apll>WS FULL</apll>) at the cost of some performance.</p> | ||
<p>VFP numbers have user-controlled variable precision, and each number may have a different precision. The default precision at startup is controlled by the value of the system variable <apll>⎕FPC</apll>. The system default of this value is <apll> | <p>VFP numbers have <b>user-controlled variable precision</b>, and each number may have a different precision. The default precision at startup is controlled by the value of the system variable <apll>⎕FPC</apll>. The system default of this value is <apll>128</apll> in units of bits of precision of the mantissa of the number, not counting the exponent (which is of fixed size). The current precision may be changed as needed by assigning a new value to the system variable. All newly created VFP numbers will have the new precision – the precision of VFP numbers already present in the workspace does not change.</p> | ||
<p>Generally, precision is set for a particular application and unchanged throughout that program. It is possible to mix VFP numbers of different precisions in a single array – presumably you really know what you are doing. The system function <apll>⎕DR</apll> can be used to display an array's precision(s).</p> | |||
===Constants=== | ===Constants=== | ||
Line 34: | Line 36: | ||
===Formatting=== | ===Formatting=== | ||
<p>Rational integers are formatted as an integer with no trailing adornment; rational non-integers are formatted as a numerator and denominator separated by an <apll>r</apll> as in <apll>34r9</apll>. As with the integer datatype, the numerator and denominator | <p>Rational integers are formatted as an integer with no trailing adornment; rational non-integers are formatted as a numerator and denominator separated by an <apll>r</apll> as in <apll>34r9</apll>. As with the integer datatype, the numerator and denominator of a rational number are formatted exactly, unaffected by <apll>⎕PP</apll>.</p> | ||
<br /> | <br /> | ||
<apll> 2*100<br /> | <apll> 2*100<br /> | ||
Line 46: | Line 48: | ||
⎕FPC←64<br /> | ⎕FPC←64<br /> | ||
○1r2<br /> | ○1r2<br /> | ||
1. | 1.57079632679489661926<br /> | ||
⎕FPC←128<br /> | ⎕FPC←128<br /> | ||
○1r2<br /> | ○1r2<br /> | ||
1. | 1.570796326794896619231321691639751442098</apll><br /> | ||
<p>where both of the above displays were limited by the precision of the number, not <apll>⎕PP</apll>.</p> | <p>where both of the above displays were limited by the precision of the number, not <apll>⎕PP</apll>.</p> | ||
Line 60: | Line 62: | ||
3.1415926535897931<br /> | 3.1415926535897931<br /> | ||
○1x<br /> | ○1x<br /> | ||
3. | 3.141592653589793238462643383279502884195</apll><br /> | ||
<p>where the datatype of the two results are floating point and VFP, respectively. That is, in a manner similar to how some primitive functions with integer arguments may return floating point results, when a rational number is used as an argument to a primitive function that can't return a result with infinite precision, it is promoted to VFP.</p> | <p>where the datatype of the two results are floating point and VFP, respectively. That is, in a manner similar to how some primitive functions with integer arguments may return floating point results, when a rational number is used as an argument to a primitive function that can't return a result with infinite precision, it is promoted to VFP.</p> | ||
<p>The reason irrational and certain other functions on rational numbers don't return rational numbers is that, by definition, the result of such a function is not representable as a rational number; instead, VFP numbers are better suited to represent irrational results where the end user may control exactly how much precision is desired.</p> | <p>The reason irrational and certain other functions on rational numbers don't return rational numbers is that, by definition, the result of such a function is, in general, not representable as a rational number; instead, VFP numbers are better suited to represent irrational results where the end user may control exactly how much precision is desired.</p> | ||
<p>Two special functions are the prime decomposition (<apll>πR</apll>)/number theoretic (<apll>LπR</apll>) functions. In order to return accurate results over a wide range of precision, all numeric arguments are converted to rational integers and the result is returned as a rational integer.</p> | <p>Two special functions are the prime decomposition (<apll>πR</apll>)/number theoretic (<apll>LπR</apll>) functions. In order to return accurate results over a wide range of precision, all numeric arguments are converted to rational integers and the result is returned as a rational integer.</p> | ||
Line 70: | Line 72: | ||
<p>The list of functions that produce VFP numbers given rational numbers is as follows:</p> | <p>The list of functions that produce VFP numbers given rational numbers is as follows:</p> | ||
* Exponentiation: <apll>*R</apll> and <apll>L*R</apll> (except when <apll>L</apll> is rational and <apll>R</apll> is a 32-bit integer in which case the result is a rational integer) | * Exponentiation: <apll>*R</apll> and <apll>L*R</apll> (except when <apll>L</apll> is rational and <apll>R</apll> is a 32-bit integer, in which case the result is a rational integer) | ||
* Logarithm: <apll>⍟R</apll> and <apll>L⍟R</apll> | * Logarithm: <apll>⍟R</apll> and <apll>L⍟R</apll> | ||
* Circle functions: <apll>L○R</apll> | * Circle functions: <apll>L○R</apll> | ||
* Root: <apll>√R</apll> and <apll>L√R</apll> | * Root: <apll>√R</apll> and <apll>L√R</apll> | ||
<p>Otherwise, rational argument(s) produce rational result(s) and VFP argument(s) produce VFP result(s).</p> | <p>Otherwise, rational argument(s) produce rational result(s) and VFP argument(s) produce VFP result(s). Of course, the dyadic scalar comparison functions still return a Boolean result regardless of the type of the arguments.</p> | ||
<p>To convert manually from one datatype to another,</p> | <p>To convert manually from one datatype to another,</p> | ||
Line 112: | Line 114: | ||
===Differences From Previous Behavior=== | ===Differences From Previous Behavior=== | ||
<ul> | |||
<li>Both monadic and dyadic query on rational integers use a built-in random number generator so as to use the entire range of arbitrary precision rational integers – this algorithm uses its own internal seeds which are much more complicated than the simple integer seed that is <apll>⎕RL</apll>.</li> | |||
<li>At the moment, matrix inverse (<apll>⌹R</apll>) and matrix division (<apll>L⌹R</apll>) on rational or VFP arguments each have two requirements: | |||
<ul> | |||
<li>for a square right argument that it be non-singular, and</li> | |||
<li>for an overdetermined (<apll>>/⍴R</apll>) right argument that the symmetric matrix <apll>(⍉R)+.×R</apll> be non-singular.</li> | |||
</ul> | |||
<p>Neither of these requirements apply to integer or floating point arguments because they already use a Singular Value Decomposition (SVD) algorithm that produces a unique result even for singular arguments (e.g., <apll>⌹5 3⍴0</apll>).</p></li> | |||
</ul> | |||
===Acknowledgments=== | ===Acknowledgments=== |
Revision as of 23:19, 31 August 2011
Introduction
Rational numbers complement the 64-bit integer datatype to provide infinite precision (or WS FULL) at the cost of some performance. Similarly, Variable-precision Floating Point (VFP) numbers complement the floating point datatype to provide more precision (controlled by the user) with a similar cost in performance.
There is no separate datatype for infinite precision Integers. Instead, they are represented as a special case of rational numbers. In the discussion below, the phrase rational integer means a rational number whose denominator is one.
Precision
Rational numbers have infinite precision. They are stored with a separate numerator and denominator, both of which are exact numbers in the sense that their size (and hence precision) may grow limited only by the available workspace. Rational numbers complement the 64-bit integer datatype to provide infinite precision (or WS FULL) at the cost of some performance.
VFP numbers have user-controlled variable precision, and each number may have a different precision. The default precision at startup is controlled by the value of the system variable ⎕FPC. The system default of this value is 128 in units of bits of precision of the mantissa of the number, not counting the exponent (which is of fixed size). The current precision may be changed as needed by assigning a new value to the system variable. All newly created VFP numbers will have the new precision – the precision of VFP numbers already present in the workspace does not change.
Generally, precision is set for a particular application and unchanged throughout that program. It is possible to mix VFP numbers of different precisions in a single array – presumably you really know what you are doing. The system function ⎕DR can be used to display an array's precision(s).
Constants
Rational constants may be entered as follows:
- 123x for the constant 123 (the suffix x is but a shorthand for r1)
- 123r4567 for the constant 123÷4567
VFP constants may be entered as follows:
- 123v for the constant 123
- 123v4567 for the constant 123.4567
- 123v4567e3 for the constant 123.4567e3
The above formats for constants (except for the suffix x) may be used in other constants such as 1r4p2 to generate a shorter and more accurate value for π2/4 than ((○1)*2)÷4.
Datatype Propagation
Generally, the datatype of constants propagates through a calculation. For example, ⍳1000 generates the first thousand integers as an integer (actually Arithmetic Progression Array) datatype, and ⍳1000x and ⍳1000v generate the same values as rational and VFP numbers, respectively.
Continuing the above example, *⍨⍳1000x generates the first thousand instances of NN as rational numbers, +/*⍨⍳1000x sums them into a single 3001-digit rational integer, and finally (10*10)|+/*⍨⍳1000x extracts the low-order ten digits – 9110846700, ProjectEuler.net #48 – all in a small number of milliseconds.
Formatting
Rational integers are formatted as an integer with no trailing adornment; rational non-integers are formatted as a numerator and denominator separated by an r as in 34r9. As with the integer datatype, the numerator and denominator of a rational number are formatted exactly, unaffected by ⎕PP.
2*100
1.2676506002282294E30
2*100x
1267650600228229401496703205376
VFP numbers are formatted as decimal numbers to the precision inherent in the number or ⎕PP, whichever is smaller. For example,
⎕PP←100
⎕FPC←64
○1r2
1.57079632679489661926
⎕FPC←128
○1r2
1.570796326794896619231321691639751442098
where both of the above displays were limited by the precision of the number, not ⎕PP.
Conversions
For the most part, rational numbers beget rational numbers and VFP numbers beget VFP numbers. However, when irrational and certain other functions are used, rational numbers beget VFP numbers. For example,
○1
3.1415926535897931
○1x
3.141592653589793238462643383279502884195
where the datatype of the two results are floating point and VFP, respectively. That is, in a manner similar to how some primitive functions with integer arguments may return floating point results, when a rational number is used as an argument to a primitive function that can't return a result with infinite precision, it is promoted to VFP.
The reason irrational and certain other functions on rational numbers don't return rational numbers is that, by definition, the result of such a function is, in general, not representable as a rational number; instead, VFP numbers are better suited to represent irrational results where the end user may control exactly how much precision is desired.
Two special functions are the prime decomposition (πR)/number theoretic (LπR) functions. In order to return accurate results over a wide range of precision, all numeric arguments are converted to rational integers and the result is returned as a rational integer.
The list of functions that produce VFP numbers given rational numbers is as follows:
- Exponentiation: *R and L*R (except when L is rational and R is a 32-bit integer, in which case the result is a rational integer)
- Logarithm: ⍟R and L⍟R
- Circle functions: L○R
- Root: √R and L√R
Otherwise, rational argument(s) produce rational result(s) and VFP argument(s) produce VFP result(s). Of course, the dyadic scalar comparison functions still return a Boolean result regardless of the type of the arguments.
To convert manually from one datatype to another,
- Integer to rational, add 0x.
- Integer or floating point number to VFP, add 0v.
- Rational integer to a 64-bit integer (possibly losing some precision), use ⍎⍕.
- Rational non-integer to a floating point number (possibly losing some precision), use ⍎⍕0v+.
- VFP number to a floating point number (possibly losing some precision), use ⍎⍕.
Comparisons
Comparisons between two rational numbers or a rational number and any other integer is exact — unaffected by the value of ⎕CT. Comparisons between a VFP number and any other number is sensitive to ⎕CT.
Datatype Demotion
It is common in APL implementations to demote datatypes where appropriate. For example, the constant 1.0 might actually be represented as an integer or even Boolean datatype. The idea is there is no loss of precision and the storage is typically smaller, so why not?
With rational and VFP numbers those reasons no longer apply. While the constant 1x might have the same precision as the constant 1.0, the difference in latent precision between the two is vast. In fact, in order for datatype propagation of rational and VFP numbers to work at all, we must be careful not to demote them to a smaller datatype. Otherwise, it would require an intolerable degree of analysis on the part of the programmer to ensure that the desired datatype (rational or VFP) remains in effect throughout a calculation.
Integer Tolerance
Both rational and VFP numbers may be used where the system ordinarily requires an integer (such axis coordinates, indexing, left argument to structural primitives, etc.) just as the system tolerates floating point numbers in those contexts if they are sufficiently near an integer. In all cases, the system attempts to convert the non-integer to an integer using the fixed system comparison tolerance (at the moment, 3E¯15).
Infinities
Support for ±∞ has been extended to rational and VFP numbers in the same manner as it applies to 64-bit integers and 64-bit floats. That is, the same cases covered by the system variable ⎕IC (Indeterminate Control) also apply to infinite rational and VFP numbers. Moreover, infinite numeric constants may be entered, for example, as
- ∞x
- ∞r1
- ∞v
- ∞v0
Also constants such as 2r∞ resolve to 0x.
Differences From Previous Behavior
- Both monadic and dyadic query on rational integers use a built-in random number generator so as to use the entire range of arbitrary precision rational integers – this algorithm uses its own internal seeds which are much more complicated than the simple integer seed that is ⎕RL.
- At the moment, matrix inverse (⌹R) and matrix division (L⌹R) on rational or VFP arguments each have two requirements:
- for a square right argument that it be non-singular, and
- for an overdetermined (>/⍴R) right argument that the symmetric matrix (⍉R)+.×R be non-singular.
Neither of these requirements apply to integer or floating point arguments because they already use a Singular Value Decomposition (SVD) algorithm that produces a unique result even for singular arguments (e.g., ⌹5 3⍴0).
Acknowledgments
The designers of J are thanked for having the foresight to include rational numbers as a separate datatype.
The following GPL libraries have been used to provide support for these datatypes:
- MPIR (Multiple Precision Integers and Rationals) at mpir.org.
- MPFR (Multiple Precision Floating-Point Reliable Library ) at mpfr.org.
- FLINT (Fast Library for Number Theory) at flintlib.org.