Quick Links: |
Provide decimal floating-point conversion functions. More...
Namespaces | |
namespace | bdldfp |
bdldfp::DecimalConvertUtil | Namespace for decimal FP conversion functions |
bdldfp::DecimalConvertUtil
, containing functions that are able to convert between the native decimal types of the platform and various other possible representations, such as binary floating-point, network encoding formats. decimalFromNetwork
and decimalToNetwork
functions can be used encode to and decode from this format. |------|----------|----------|-----|----------|----------------|
| size | S (bits) | E (bits) | B | T (bits) | max signficand |
|------|----------|----------|-----|----------|----------------|
| 1* | 0 | 1 | -2 | 7 | 127 |
| 2 | 0 | 2 | -3 | 14 | 16383 |
| 3 | 0 | 3 | -6 | 21 | 2097151 |
| 4 | 1 | 5 | -16 | 26 | 67108863 |
| 5 | 1 | 5 | -16 | 34 | 17179869183 |
|------|-------------------------------------------------------|
| 8 | FULL IEEE INTERCHANGE FORMAT** |
|------|-------------------------------------------------------|
S = sign, E = exponent, B = bias, T = significant
1 byte encoding will be supported by the decoder but not the encoder. This
is done due to the relatively large performance impact of adding the 1
byte encoding to the encoder (10%). Preserving the encoding size in the
decoder allows us to easily enable this encoding size at a later time.
If the value to be encoded can not fit in the 5-byte encoding or is -Inf,
+Inf, or Nan, then the full 8-byte IEEE format will be used.
decimal64ToMultiWidthEncoding
and decimal64FromMultiWidthEncoding
can be used to encode to and decode from this format. Currently, only 64-bit decimal values are supported by this encoding format. |------|------------|---|---|-----|----|-----------------| | size | P | S | E | B | T | max significant | |------|------------|---|---|-----|----|-----------------| | 2 | 0b0 | 0 | 2 | -2 | 13 | 8191 | | 3 | 0b10 | 0 | 3 | -4 | 19 | 524287 | | 4 | 0b11 | 1 | 5 | -16 | 24 | 16777215 | |------|------------|------------------------------------| | 9 | 0b11111111 | FULL IEEE FORMAT* | |------|------------|------------------------------------| P = predicate (bit values) S = sign (bits), E = exponent (bits), B = bias T = significant (bits) If the value to be encoded can not fit in the 4-byte encoding or is -Inf, +Inf, or Nan, then the full 8-byte IEEE format will be used prefixed by a 1 byte predicate having the value of 0xFF.
decimal64ToVariableWidthEncoding
and decimal64FromVariableWidthEncoding
can be used to encode to and decode from this format. Currently, only 64-bit decimal values are supported by this encoding format. DecimalConvertUtil::decimalToDouble
), the result is the representable binary number nearest in value to that decimal. If there are two such, the one whose least significant bit is 0 is generally chosen. (This is also true for conversion of decimals to 32-bit Perkin-Elmer format, but when the Perkin-Elmer float is then converted to IEEE float, the resulting value is not the same as converting the decimal to IEEE float directly, because the Perkin-Elmer format can lose up to three bits of representation compared to the IEEE format.) Unless the decimal value is exactly a multiple of a power of two (e.g., 3.4375 = 55 * 1/16), the converted binary value cannot equal the decimal value, it can only be close. This utility provides decimal{,32,64,128}To{Float,Double}
functions to convert decimal floating-point numbers to their closest binary floating-point values. float
values, but 8589973000 and 8589974000, with 7 significant digits, convert to the same float
value. Similarly, all decimals with 15 significant digits convert to unique double
values but 900719925474.0992 and 900719925474.0993, with 16 significant digits, convert to the same double
value. Over restricted ranges, the maximum may be higher - for example, every decimal value with 7 or fewer significant digits between 1e-3 and 8.5e9 converts to a unique float
value. Express the value as its nearest decimal value.
Decimal{32,64,128}(value)
Express the value rounded to a given number of significant digits. (The significant digits of a decimal number are the digits with all leading and trailing 0s removed; e.g., 0.00103, 10.3 and 10300 each have 3 significant digits.) This conversion is the one that leads programmers to complain about "rounding error" (for example, .1f rounded to 9 digits is .100000001) but is the appropriate one to use when the programmer knows that the binary value was originally converted from a decimal value with that many significant digits.
Decimal{32,64,128}From{Float,Double}(value, digits)
Express the value using the minimum number of significant digits for the type of the binary such that converting the decimal value back to binary will yield the same value. (Note that 17 digits are needed for double
and 9 for float
, so not all decimal types can hold such a result.)
Decimal{64,128}FromFloat(value, 9)
or Decimal128FromDouble(value, 17)
Express the value using a number of decimal places that restores the original decimal value from which the binary value was converted, assuming that the original decimal value had sufficiently few significant digits so that no two values with that number of digits would convert to the same binary value. (That number is 15 for double
and 6 for float
in general but 7 over a limited range that spans [1e-3 .. 8.5e9]
).
Decimal{32,64,128}From{Float,Double}(value)
Express the value as the shortest decimal number that converts back exactly to the binary value. For example. given the binary value 0x3DCCCCCD above, that corresponding shortest decimal value is (unsurprisingly) .1, while the next lower value 0x3DCCCCCC has the shortest decimal .099999994 and the next higher value 0x3DCCCCCE has the shortest decimal .010000001. This is the most visually appealing result, but can be expensive and slow to compute.
Decimal{32,64,128}From{Float,Double}(value, -1)
Express the value using a number of decimal places that restores the original decimal value assuming that it is a float
which originated as an IBM/Perkin-Elmer/Interdata float
value itself originally converted from a decimal value.
Decimal{32,64,128}FromFloat(value, 6)
Express the value exactly as a decimal. For example, the decimal value .1 converts to the 32-bit IEEE float value 0x3DCCCCCD, which has the exact value .100000001490116119384765625. This conversion is seldom useful, except perhaps for debugging, since the exact value may have over 1000 digits, and as well cannot be represented as a decimal floating-point type since those types do not have enough digits.
sprintf
into a large-enough buffer: char buf[2000]; double value; sprintf(buf, "%.1100f", value);
Express the value rounded to a given number of decimal places. (The decimal places of a decimal number are the number of digits after the decimal point, with trailing 0s removed; .01, 10.01, and 1000.01 each have two decimal places.) This conversion can be problematic when the integer portion of the value is large, as there may not be enough precision remaining to deliver a meaningful number of decimal places. As seen above, for example, for numbers near one trillion, there is not enough precision in a double
for 4 decimal places.
sprintf
into a large-enough buffer: char buf[2000]; double value; sprintf(buf, "%.*f", places, value);
unsigned char msgbuffer[256]; unsigned char *next = msgbuffer; BDEC::Decimal64 number(BDLDFP_DECIMAL_DD(1.234567890123456e-42)); unsigned char expected[] = { 0x25, 0x55, 0x34, 0xb9, 0xc1, 0xe2, 0x8e, 0x56 }; next = bdldfp::DecimalConvertUtil::decimalToNetwork(next, number); assert(memcmp(msgbuffer, expected, sizeof(number)) == 0);
unsigned char msgbuffer[] ={ 0x25, 0x55, 0x34, 0xb9, 0xc1, 0xe2, 0x8e, 0x56 }; unsigned char *next = msgbuffer; BDEC::Decimal64 number; BDEC::Decimal64 expected(BDLDFP_DECIMAL_DD(1.234567890123456e-42)); next = bdldfp::DecimalConvertUtil::decimalFromNetwork(number, next); assert(number == expected);
const BDEC::Decimal64 number(BDLDFP_DECIMAL_DD(1.23456789012345e-42)); typedef bdldfp::DecimalConvertUtil Util; double dbl = Util::decimalToDouble(number); if (Util::decimal64FromDouble(dbl) != number) { // Do what is appropriate for the application }
decimal64FromDouble
function: BDEC::Decimal64 restored = Util::decimal64FromDouble(dbl); assert(number == restored);