Expand description
A format for exchanging arbitrary precision decimal floating-point numbers.
This library converts text-based numbers like -123.456e7
into bitstrings like 01010110_10001110_10010010_10100010
.
Specifically, it implements support for an IEEE 754 compatible decimal floating-point bitstring, using densely-packed-decimal encoding, in little-endian byte-order. Along with the bitstring encoding there is an equivalent text-based one that can represent all the same values. High-level types that convert between these two formats and standard Rust numeric types are provided.
Encoding
The following table demonstrates how various numbers are encoded as 32bit decimals by this library to give you an idea of how the format works:
text | binary |
---|---|
bit layout | tttttttt_tttttttt_ggggtttt_sggggggg |
0 | 00000000_00000000_01010000_00100010 |
-0 | 00000000_00000000_01010000_10100010 |
0e1 | 00000000_00000000_01100000_00100010 |
123 | 10100011_00000000_01010000_00100010 |
-123 | 10100011_00000000_01010000_10100010 |
123.456 | 01010110_10001110_00100010_00100010 |
-123.456 | 01010110_10001110_00100010_10100010 |
inf | 00000000_00000000_00000000_01111000 |
-inf | 00000000_00000000_00000000_11111000 |
nan | 00000000_00000000_00000000_01111100 |
snan | 00000000_00000000_00000000_01111110 |
-nan | 00000000_00000000_00000000_11111100 |
-snan | 00000000_00000000_00000000_11111110 |
nan(123) | 10100011_00000000_00000000_01111100 |
snan(123) | 10100011_00000000_00000000_01111110 |
where:
s
: The sign bit.g
: The combination field.t
: The trailing significand.
Note that this library always encodes in little-endian byte-order, regardless of the endianness of the underlying platform.
Also note, this encoding is different on big-endian platforms than libdecimal
’s internal encoding, which isn’t specified, but
currently uses arrays of 32bit integers.
More sizes besides 32bit are supported. The table uses it to minimize space.
Why decimal bitstrings?
The decimal bitstrings specified in IEEE 754 aren’t as widely known as their binary counterparts, but are a good target for exchanging numbers.
Compared with text, decimal bitstrings are:
- Compact. Instead of encoding 1 digit per byte (8 bits), you get 3 digits per 10 bits.
- Cheap to classify. You can tell from a single byte whether or not a number is positive, negative, whole, infinity, or NaN. You don’t need to reparse the number.
Compared with binary (base-2) bitstrings, decimal bitstrings are:
- Easy to convert between text. You don’t need arbitrary-precision arithmetic to encode a human-readable number into a decimal bitstring.
- Precise. You can exactly encode base-10 numbers, which is the base most modern number systems use.
- Consistent. They’re a newer standard, so they avoid some ambiguities around NaN payloads and signaling that affect the portability of binary bitstrings.
Features and limitations
This library only does conversions between Rust’s primitive number types, numbers encoded as text, and decimal bitstrings.
It’s not an implementation of decimal arithmetic. It also doesn’t do rounding. If a number can’t be encoded in a decimal
bitstring of a given width then you’ll get None
s instead of infinities or rounded values.
Decimal numbers in IEEE 754 are non-normalized by-design. The number 1.00
will encode differently to 1
or 1.0
.
This library does support very high precision in no-std, and can work with arbitrary precision when the
arbitrary-precision
feature is enabled.
Conversions
Binary floating point
This library can convert binary floating points (f32
and f64
) into decimals.
It uses ryū to pick an appropriate decimal representation and faithfully encodes that.
The following cases are worth calling out:
0f64
will encode as0.0
, which is different to0
.- A signaling NaN is encoded as a quiet NaN.
- NaN payloads are discarded.
Exponents
The exponent range of a decimal depends on its width in bits. Wider decimals support a wider exponent range. The actual exponent you can write in a decimal also depends on whether the number is fractional. For example, the following all encode the same number at the edge of decimal64’s exponent range:
100e369
10.0e370
1.00e371
Structs
- An arbitrary precision decimal number.
- A dynamically sized decimal number with enough precision to fit any Rust primitive number.
- An error encountered converting between decimals and primitive types.
- An error encountered while working with decimals.
- An error encountered creating a buffer to encode a decimal into.
- An error encountered parsing a decimal from text.