Crate decstr

source ·
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:

textbinary
bit layouttttttttt_tttttttt_ggggtttt_sggggggg
000000000_00000000_01010000_00100010
-000000000_00000000_01010000_10100010
0e100000000_00000000_01100000_00100010
12310100011_00000000_01010000_00100010
-12310100011_00000000_01010000_10100010
123.45601010110_10001110_00100010_00100010
-123.45601010110_10001110_00100010_10100010
inf00000000_00000000_00000000_01111000
-inf00000000_00000000_00000000_11111000
nan00000000_00000000_00000000_01111100
snan00000000_00000000_00000000_01111110
-nan00000000_00000000_00000000_11111100
-snan00000000_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 Nones 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 as 0.0, which is different to 0.
  • 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