Numbers¶
RCL features a single number type that can represent both integers, and numbers with a decimal part.
- Numbers are decimals with a finite range.
- Numbers track the position of the decimal point. In particular, RCL will never turn floats (numbers that have a decimal point and/or exponent) into integers in the output.
- Arithmetic is exact or fails, but never silently inexact.
For reference documentation of the supported methods, see the Number
type in the language reference. For background about why numbers work the way they do in RCL, see the blog post A float walks into a gradual type system.
Syntax¶
RCL supports the same number formats as json:
- Decimal integers, e.g.
42
. - Numbers with decimal point e.g.
42.0
, optionally with exponent, e.g.0.42e2
.
Aside from decimal numbers, RCL supports integers in other bases:
- Hexadecimal with
0x
prefix, e.g.0x2a
. - Binary with
0b
prefix, e.g.0b101010
.
Numbers may contain underscores for readability, e.g. 100_000.000_000
.
Precision¶
Numbers support up to 19 significant decimal digits, see representation below for the technical details.
For input, if the source file contains more significant digits than what RCL can represent, it will round to the nearest representable number for numbers with a decimal point or exponent,1 and it will report a range error for integers.
For arithmetic, when the result cannot be represented exactly, RCL will fail with an error. In that case, explicitly rounding the number before performing arithmetic can help to bring the result back into representable range.
Representation¶
Numbers in RCL are rational numbers of the form m × 10 n – d.
- The mantissa, m, is a signed 64-bit integer.
- The exponent, n, is a signed 16-bit integer.
- The number of decimals, d, is an unsigned 8-bit integer.
This representation enables the following:
- All signed 64-bit integers can be represented exactly.
- We can track the position of the decimal point. 1 is represented as 1 × 10 0 – 0, 1.0 is represented as 10 × 10 0 – 1, 1.00 is represented as 100 × 10 0 – 2, etc.
- We can distinguish between
1.0
and10e-1
, even though both have a mantissa of 10. - Together, this means that RCL can preserve the core formatting of numbers, which ensures that numbers can be losslessly transferred from input to output. In particular, RCL does not remove the decimal point from numbers that happen to be integral.
Note, numbers are not IEEE floats. In particular, subtleties such as NaN, infinities, and negative zero do not exist in RCL. Where float arithmetic would produce such values, RCL reports an error instead.
This is the pragmatic choice. If RCL is used to query json documents, accepting an input encountered in the wild is more useful than rejecting it. Applications that care about excessive significant digits, serialize numbers as strings anyway, because many json parsers will parse numbers into 64-bit floats, which have less precision than RCL numbers. ↩