|
| 1 | +// Copyright 2025 Matt Borland |
| 2 | +// Distributed under the Boost Software License, Version 1.0. |
| 3 | +// https://www.boost.org/LICENSE_1_0.txt |
| 4 | +// |
| 5 | +// This file demonstrates how to convert various types to decimal types and back, |
| 6 | +// along with edge case handling |
| 7 | + |
| 8 | +#include <boost/decimal/decimal32_t.hpp> // For type decimal32_t |
| 9 | +#include <boost/decimal/decimal64_t.hpp> // For type decimal64_t |
| 10 | +#include <boost/decimal/cmath.hpp> // For decimal support of cmath functions |
| 11 | +#include <boost/decimal/iostream.hpp> // For decimal support of <iostream> and <iomanip> |
| 12 | +#include <boost/decimal/numbers.hpp> // For decimal support of <numbers> |
| 13 | +#include <iostream> |
| 14 | +#include <cmath> |
| 15 | +#include <limits> |
| 16 | + |
| 17 | +int main() |
| 18 | +{ |
| 19 | + using boost::decimal::decimal32_t; // Type decimal32_t |
| 20 | + using boost::decimal::decimal64_t; // Type decimal64_t |
| 21 | + |
| 22 | + // Non-finite values construct the equivalent non-finite value in binary floating point |
| 23 | + constexpr decimal64_t decimal_qnan {std::numeric_limits<decimal64_t>::quiet_NaN()}; |
| 24 | + const double double_from_qnan {static_cast<double>(decimal_qnan)}; |
| 25 | + |
| 26 | + // Note here that we must use boost::decimal::isnan for decimal types, |
| 27 | + // as it is illegal to overload std::isnan |
| 28 | + if (boost::decimal::isnan(decimal_qnan) && std::isnan(double_from_qnan)) |
| 29 | + { |
| 30 | + std::cout << "Decimal QNAN converts to double QNAN\n"; |
| 31 | + } |
| 32 | + |
| 33 | + constexpr decimal64_t decimal_inf {std::numeric_limits<decimal64_t>::infinity()}; |
| 34 | + const double double_from_inf {static_cast<double>(decimal_inf)}; |
| 35 | + |
| 36 | + // Same as the above but with INF instead of NAN |
| 37 | + if (boost::decimal::isinf(decimal_inf) && std::isinf(double_from_inf)) |
| 38 | + { |
| 39 | + std::cout << "Decimal INFINITY converts to double INFINITY\n"; |
| 40 | + } |
| 41 | + |
| 42 | + // For finite values we make a best effort approach to covert to double |
| 43 | + // We are able to decompose the decimal floating point value into a sign, significand, and exponent. |
| 44 | + // From there we use the methods outline in Daniel Lemire's "Number Parsing at a Gigabyte a Second", |
| 45 | + // to construct the binary floating point value. |
| 46 | + // See: https://arxiv.org/pdf/2101.11408 |
| 47 | + |
| 48 | + // Construct the decimal64_t version of pi using our pre-computed constants from <boost/decimal/numbers.hpp> |
| 49 | + constexpr decimal64_t decimal_pi {boost::decimal::numbers::pi_v<decimal64_t>}; |
| 50 | + const double double_from_pi {static_cast<double>(decimal_pi)}; |
| 51 | + |
| 52 | + std::cout << std::setprecision(std::numeric_limits<decimal64_t>::digits10) |
| 53 | + << "decimal64_t pi: " << decimal_pi << '\n' |
| 54 | + << " double pi: " << double_from_pi << '\n'; |
| 55 | + |
| 56 | + // To construct a decimal64_t from double we use the methods described in "Ryu: fast float-to-string conversion" |
| 57 | + // See: https://dl.acm.org/doi/10.1145/3192366.3192369 |
| 58 | + // This paper shows how to decompose a double into it's sign, significand, and exponent |
| 59 | + // Once we have those components we can use the normal constructors of the decimal types to construct |
| 60 | + // Since we are using the normal constructors here, |
| 61 | + // any construction from this conversion is subject to the current rounding mode |
| 62 | + // Such as with a lossy conversion like shown (double -> decimal32_t) |
| 63 | + |
| 64 | + const decimal64_t decimal_from_double {static_cast<decimal64_t>(double_from_pi)}; |
| 65 | + const decimal32_t lossy_decimal_from_double {static_cast<decimal32_t>(double_from_pi)}; |
| 66 | + |
| 67 | + std::cout << " Converted pi: " << decimal_from_double << '\n' |
| 68 | + << "decimal32_t pi: " << lossy_decimal_from_double << '\n'; |
| 69 | + |
| 70 | + |
| 71 | + // Other than what has already been shown, |
| 72 | + // there are no other ways in the library to convert between decimal types and binary floating point types |
| 73 | + // The reason for this is to discourage their use. |
| 74 | + // |
| 75 | + // You can use intermediate representations like strings if you want to make these conversions, |
| 76 | + // and want to be sure about what the resulting value will be |
| 77 | + |
| 78 | + return 0; |
| 79 | +} |
0 commit comments