summaryrefslogtreecommitdiff
path: root/src/third_party/libmongocrypt/dist/src/mc-dec128.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/third_party/libmongocrypt/dist/src/mc-dec128.h')
-rw-r--r--src/third_party/libmongocrypt/dist/src/mc-dec128.h684
1 files changed, 684 insertions, 0 deletions
diff --git a/src/third_party/libmongocrypt/dist/src/mc-dec128.h b/src/third_party/libmongocrypt/dist/src/mc-dec128.h
new file mode 100644
index 00000000000..c609b57b0f7
--- /dev/null
+++ b/src/third_party/libmongocrypt/dist/src/mc-dec128.h
@@ -0,0 +1,684 @@
+#ifndef MC_DEC128_H_INCLUDED
+#define MC_DEC128_H_INCLUDED
+
+#include <bson/bson.h>
+
+#include <mlib/macros.h>
+#include <mlib/int128.h>
+#include <mlib/endian.h>
+
+// Include the header that declares the DFP functions, which may be macros that
+// expand to renamed symbols:
+#include <bid_conf.h>
+
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <float.h>
+
+MLIB_C_LINKAGE_BEGIN
+
+/// Rounding controls for Decimal128 operations
+typedef enum mc_dec128_rounding_mode {
+ MC_DEC128_ROUND_NEAREST_EVEN = 0,
+ MC_DEC128_ROUND_DOWNWARD = 1,
+ MC_DEC128_ROUND_UPWARD = 2,
+ MC_DEC128_ROUND_TOWARD_ZERO = 3,
+ MC_DEC128_ROUND_NEAREST_AWAY = 4,
+ MC_DEC128_ROUND_DEFAULT = MC_DEC128_ROUND_NEAREST_EVEN,
+} mc_dec128_rounding_mode;
+
+typedef struct mc_dec128_flagset {
+ int bits;
+} mc_dec128_flagset;
+
+// This alignment conditional is the same conditions used in Intel's DFP
+// library, ensuring we match the ABI of the library without pulling the header
+#if defined _MSC_VER
+#if defined _M_IX86 && !defined __INTEL_COMPILER
+#define _mcDec128Align(n)
+#else
+#define _mcDec128Align(n) __declspec(align (n))
+#endif
+#else
+#if !defined HPUX_OS
+#define _mcDec128Align(n) __attribute__ ((aligned (n)))
+#else
+#define _mcDec128Align(n)
+#endif
+#endif
+
+typedef union _mcDec128Align (16)
+{
+ uint64_t _words[2];
+#if !defined(__INTELLISENSE__) && defined(__GNUC__) && defined(__amd64) && \
+ !defined(__APPLE__) && !defined(__clang__)
+ // If supported by the compiler, emit a field that can be used to visualize
+ // the value in a debugger.
+ float value_ __attribute__ ((mode (TD)));
+#endif
+}
+mc_dec128;
+
+#undef _mcDec128Align
+
+/// Expands to a dec128 constant value.
+#ifdef __cplusplus
+#define MC_DEC128_C(N) \
+ mc_dec128 _mcDec128Const (((N) < 0 ? -(N) : (N)), ((N) < 0 ? 1 : 0))
+#else
+#define MC_DEC128_C(N) \
+ _mcDec128Const (((N) < 0 ? -(N) : (N)), ((N) < 0 ? 1 : 0))
+#endif
+
+#define MC_DEC128(N) MLIB_INIT (mc_dec128) MC_DEC128_C (N)
+
+#define _mcDec128Combination(Bits) ((uint64_t) (Bits) << (47))
+#define _mcDec128ZeroExpCombo _mcDec128Combination (1 << 7 | 1 << 13 | 1 << 14)
+#define _mcDec128Const(N, Negate) \
+ _mcDec128ConstFromParts ( \
+ N, (_mcDec128ZeroExpCombo | ((uint64_t) (Negate) << 63)))
+#define _mcDec128ConstFromParts(CoeffLow, HighWord) \
+ { \
+ { \
+ MLIB_IS_LITTLE_ENDIAN ? (uint64_t) (CoeffLow) \
+ : (uint64_t) (HighWord), \
+ MLIB_IS_LITTLE_ENDIAN ? (uint64_t) (HighWord) \
+ : (uint64_t) (CoeffLow), \
+ }, \
+ }
+
+static const mc_dec128 MC_DEC128_ZERO = MC_DEC128_C (0);
+static const mc_dec128 MC_DEC128_ONE = MC_DEC128_C (1);
+static const mc_dec128 MC_DEC128_MINUSONE = MC_DEC128_C (-1);
+
+/// The greatest-magnitude finite negative value representable in a Decimal128
+#define MC_DEC128_LARGEST_NEGATIVE \
+ mc_dec128_from_string ("-9999999999999999999999999999999999E6111")
+/// The least-magnitude non-zero negative value representable in a Decimal128
+#define MC_DEC128_SMALLEST_NEGATIVE mc_dec128_from_string ("-1E-6176")
+/// The greatest-magnitude finite positive value representable in a Decimal128
+#define MC_DEC128_LARGEST_POSITIVE \
+ mc_dec128_from_string ("9999999999999999999999999999999999E6111")
+/// The least-magnitude non-zero positive value representable in a Decimal128
+#define MC_DEC128_SMALLEST_POSITIVE mc_dec128_from_string ("1E-6176")
+/// The normalized zero of Decimal128
+#define MC_DEC128_NORMALIZED_ZERO MC_DEC128_C (0)
+/// A zero of Decimal128 with the least exponent
+#define MC_DEC128_NEGATIVE_EXPONENT_ZERO mc_dec128_from_string ("0E-6176")
+#define _mcDec128InfCombo \
+ _mcDec128Combination (1 << 15 | 1 << 14 | 1 << 13 | 1 << 12)
+#define _mcDec128QuietNaNCombo \
+ _mcDec128Combination (1 << 15 | 1 << 14 | 1 << 13 | 1 << 12 | 1 << 11)
+
+/// Positive infinity of Decimal128
+#define MC_DEC128_POSITIVE_INFINITY \
+ _mcDec128ConstFromParts (0, _mcDec128InfCombo)
+/// Negative infinity of Decimal128
+#define MC_DEC128_NEGATIVE_INFINITY \
+ _mcDec128ConstFromParts (0, _mcDec128InfCombo | 1ull << 63)
+/// Positve quiet NaN of Decimal128
+#define MC_DEC128_POSITIVE_NAN \
+ _mcDec128ConstFromParts (0, _mcDec128QuietNaNCombo)
+/// Negative quiet NaN of Decimal128
+#define MC_DEC128_NEGATIVE_NAN \
+ _mcDec128ConstFromParts (0, _mcDec128QuietNaNCombo | 1ull << 63)
+
+
+/**
+ * @brief Convert a double-precision binary floating point value into the
+ * nearest Decimal128 value
+ *
+ * @param d The number to conver
+ * @param rnd The rounding mode in case the value is not exactly representable
+ * @param flags Out param for exception/error flags (Optional)
+ */
+static inline mc_dec128
+mc_dec128_from_double_ex (double d,
+ mc_dec128_rounding_mode rnd,
+ mc_dec128_flagset *flags)
+{
+ extern mc_dec128 binary64_to_bid128 (
+ double d, mc_dec128_rounding_mode, mc_dec128_flagset *);
+ mc_dec128_flagset zero_flags = {0};
+ return binary64_to_bid128 (d, rnd, flags ? flags : &zero_flags);
+}
+
+/**
+ * @brief Convert a double-precision binary floating point value into the
+ * nearest Decimal128 value
+ */
+static inline mc_dec128
+mc_dec128_from_double (double d)
+{
+ return mc_dec128_from_double_ex (d, MC_DEC128_ROUND_DEFAULT, NULL);
+}
+
+/**
+ * @brief Convert a string representation of a number into the nearest
+ * Decimal128 value
+ *
+ * @param s The string to parse. MUST be null-terminated
+ * @param rnd The rounding mode to use if the result is not representable
+ * @param flags Out param for exception/error flags (Optional)
+ */
+static inline mc_dec128
+mc_dec128_from_string_ex (const char *s,
+ mc_dec128_rounding_mode rnd,
+ mc_dec128_flagset *flags)
+{
+ extern mc_dec128 bid128_from_string (
+ const char *, mc_dec128_rounding_mode, mc_dec128_flagset *);
+ mc_dec128_flagset zero_flags = {0};
+ return bid128_from_string (s, rnd, flags ? flags : &zero_flags);
+}
+
+/**
+ * @brief Convert a string representation of a number into the nearest
+ * Decimal128 value
+ */
+static inline mc_dec128
+mc_dec128_from_string (const char *s)
+{
+ return mc_dec128_from_string_ex (s, MC_DEC128_ROUND_DEFAULT, NULL);
+}
+
+/**
+ * @brief A type capable of holding a string rendering of a Decimal128 in
+ * engineering notation.
+ */
+typedef struct mc_dec128_string {
+ /// The character array of the rendered value. Null-terminated
+ char str[48];
+} mc_dec128_string;
+
+/**
+ * @brief Render a Decimal128 value as a string (in engineering notation)
+ *
+ * @param d The number to represent
+ * @param flags Output parameter for exception/error flags (optional)
+ */
+static inline mc_dec128_string
+mc_dec128_to_string_ex (mc_dec128 d, mc_dec128_flagset *flags)
+{
+ extern void bid128_to_string (char *, mc_dec128 d, mc_dec128_flagset *);
+ mc_dec128_flagset zero_flags = {0};
+ mc_dec128_string out = {{0}};
+ bid128_to_string (out.str, d, flags ? flags : &zero_flags);
+ return out;
+}
+
+/**
+ * @brief Render a Decimal128 value as a string (in engineering notation)
+ */
+static inline mc_dec128_string
+mc_dec128_to_string (mc_dec128 d)
+{
+ return mc_dec128_to_string_ex (d, NULL);
+}
+
+/// Compare two dec128 numbers
+#define DECL_IDF_COMPARE_1(Oper) \
+ static inline bool mc_dec128_##Oper##_ex ( \
+ mc_dec128 left, mc_dec128 right, mc_dec128_flagset *flags) \
+ { \
+ extern int bid128_quiet_##Oper ( \
+ mc_dec128 left, mc_dec128 right, mc_dec128_flagset *); \
+ mc_dec128_flagset zero_flags = {0}; \
+ return 0 != \
+ bid128_quiet_##Oper (left, right, flags ? flags : &zero_flags); \
+ } \
+ \
+ static inline bool mc_dec128_##Oper (mc_dec128 left, mc_dec128 right) \
+ { \
+ return mc_dec128_##Oper##_ex (left, right, NULL); \
+ }
+
+#define DECL_IDF_COMPARE(Op) \
+ DECL_IDF_COMPARE_1 (Op) \
+ DECL_IDF_COMPARE_1 (not_##Op)
+
+DECL_IDF_COMPARE (equal)
+DECL_IDF_COMPARE (greater)
+DECL_IDF_COMPARE (greater_equal)
+DECL_IDF_COMPARE (less)
+DECL_IDF_COMPARE (less_equal)
+
+#undef DECL_IDF_COMPARE
+#undef DECL_IDF_COMPARE_1
+
+/// Test properties of Decimal128 numbers
+#define DECL_PREDICATE(Name, BIDName) \
+ static inline bool mc_dec128_##Name (mc_dec128 d) \
+ { \
+ extern int bid128_##BIDName (mc_dec128 d); \
+ return 0 != bid128_##BIDName (d); \
+ }
+
+DECL_PREDICATE (is_zero, isZero)
+DECL_PREDICATE (is_negative, isSigned)
+DECL_PREDICATE (is_inf, isInf)
+DECL_PREDICATE (is_finite, isFinite)
+DECL_PREDICATE (is_nan, isNaN)
+
+#undef DECL_PREDICATE
+
+/// Binary arithmetic operations on two Decimal128 numbers
+#define DECL_IDF_BINOP_WRAPPER(Oper) \
+ static inline mc_dec128 mc_dec128_##Oper##_ex ( \
+ mc_dec128 left, \
+ mc_dec128 right, \
+ mc_dec128_rounding_mode mode, \
+ mc_dec128_flagset *flags) \
+ { \
+ extern mc_dec128 bid128_##Oper (mc_dec128 left, \
+ mc_dec128 right, \
+ mc_dec128_rounding_mode rounding, \
+ mc_dec128_flagset *flags); \
+ mc_dec128_flagset zero_flags = {0}; \
+ return bid128_##Oper (left, right, mode, flags ? flags : &zero_flags); \
+ } \
+ \
+ static inline mc_dec128 mc_dec128_##Oper (mc_dec128 left, mc_dec128 right) \
+ { \
+ return mc_dec128_##Oper##_ex ( \
+ left, right, MC_DEC128_ROUND_DEFAULT, NULL); \
+ }
+
+DECL_IDF_BINOP_WRAPPER (add)
+DECL_IDF_BINOP_WRAPPER (mul)
+DECL_IDF_BINOP_WRAPPER (div)
+DECL_IDF_BINOP_WRAPPER (sub)
+DECL_IDF_BINOP_WRAPPER (pow)
+
+#undef DECL_IDF_BINOP_WRAPPER
+
+/// Unary arithmetic operations on two Decimal128 numbers
+#define DECL_IDF_UNOP_WRAPPER(Oper) \
+ static inline mc_dec128 mc_dec128_##Oper##_ex (mc_dec128 operand, \
+ mc_dec128_flagset *flags) \
+ { \
+ extern mc_dec128 bid128_##Oper ( \
+ mc_dec128 v, mc_dec128_rounding_mode, mc_dec128_flagset *); \
+ mc_dec128_flagset zero_flags = {0}; \
+ return bid128_##Oper ( \
+ operand, MC_DEC128_ROUND_DEFAULT, flags ? flags : &zero_flags); \
+ } \
+ \
+ static inline mc_dec128 mc_dec128_##Oper (mc_dec128 operand) \
+ { \
+ return mc_dec128_##Oper##_ex (operand, NULL); \
+ }
+
+DECL_IDF_UNOP_WRAPPER (round_integral_zero)
+DECL_IDF_UNOP_WRAPPER (round_integral_positive)
+DECL_IDF_UNOP_WRAPPER (round_integral_negative)
+DECL_IDF_UNOP_WRAPPER (round_integral_exact)
+DECL_IDF_UNOP_WRAPPER (log2)
+DECL_IDF_UNOP_WRAPPER (log10)
+DECL_IDF_UNOP_WRAPPER (negate)
+DECL_IDF_UNOP_WRAPPER (abs)
+#undef DECL_IDF_UNOP_WRAPPER
+
+/**
+ * @brief Scale the given Decimal128 by a power of ten
+ *
+ * @param fac The value being scaled
+ * @param exp The exponent: 10^exp is the scale factor
+ * @param rounding Rounding behavior
+ * @param flags Control/result flags
+ * @return mc_dec128 The product
+ */
+static inline mc_dec128
+mc_dec128_scale_ex (mc_dec128 fac,
+ long int exp,
+ mc_dec128_rounding_mode rounding,
+ mc_dec128_flagset *flags)
+{
+ extern mc_dec128 bid128_scalbln (
+ mc_dec128 fac, long int, mc_dec128_rounding_mode, mc_dec128_flagset *);
+ mc_dec128_flagset zero_flags = {0};
+ return bid128_scalbln (fac, exp, rounding, flags ? flags : &zero_flags);
+}
+
+/**
+ * @brief Scale the given Decimal128 by a power of ten
+ *
+ * @param fac The value being scaled
+ * @param exp The exponent: 10^exp is the scale factor
+ * @return mc_dec128 The product
+ */
+static inline mc_dec128
+mc_dec128_scale (mc_dec128 fac, long int exp)
+{
+ return mc_dec128_scale_ex (fac, exp, MC_DEC128_ROUND_DEFAULT, NULL);
+}
+
+/// The result of a dec_128 modf operation
+typedef struct mc_dec128_modf_result {
+ /// The whole part of the result
+ mc_dec128 whole;
+ /// The fractional part of the result
+ mc_dec128 frac;
+} mc_dec128_modf_result;
+
+/**
+ * @brief Split a Decimal128 into its whole and fractional parts.
+ *
+ * The "whole" value is the integral value of the Decimal128 rounded towards
+ * zero. The "frac" part is the remainder after removing the whole.
+ *
+ * @param d The value to inspect
+ * @param flags Results status flags
+ */
+static inline mc_dec128_modf_result
+mc_dec128_modf_ex (mc_dec128 d, mc_dec128_flagset *flags)
+{
+ extern mc_dec128 bid128_modf (
+ mc_dec128 d, mc_dec128 * iptr, mc_dec128_flagset *);
+ mc_dec128_flagset zero_flags = {0};
+ mc_dec128_modf_result res;
+ res.frac = bid128_modf (d, &res.whole, flags ? flags : &zero_flags);
+ return res;
+}
+
+/**
+ * @brief Split a Decimal128 into its whole and fractional parts.
+ *
+ * The "whole" value is the integral value of the Decimal128 rounded towards
+ * zero. The "frac" part is the remainder after removing the whole.
+ *
+ * @param d The value to inspect
+ */
+static inline mc_dec128_modf_result
+mc_dec128_modf (mc_dec128 d)
+{
+ return mc_dec128_modf_ex (d, NULL);
+}
+
+/**
+ * @brief Compute the "fmod", the remainder after decimal division rounding
+ * towards zero.
+ *
+ * @param numer The dividend of the modulus
+ * @param denom The divisor of the modulus
+ * @param flags Control/status flags
+ */
+static inline mc_dec128
+mc_dec128_fmod_ex (mc_dec128 numer, mc_dec128 denom, mc_dec128_flagset *flags)
+{
+ extern mc_dec128 bid128_fmod (
+ mc_dec128 numer, mc_dec128 denom, mc_dec128_flagset *);
+ mc_dec128_flagset zero_flags = {0};
+ return bid128_fmod (numer, denom, flags ? flags : &zero_flags);
+}
+
+/**
+ * @brief Compute the "fmod", the remainder after decimal division rounding
+ * towards zero.
+ *
+ * @param numer The dividend of the modulus
+ * @param denom The divisor of the modulus
+ */
+static inline mc_dec128
+mc_dec128_fmod (mc_dec128 numer, mc_dec128 denom)
+{
+ return mc_dec128_fmod_ex (numer, denom, NULL);
+}
+
+/**
+ * @brief Obtain the a 64-bit binary integer value for the given Decimal128
+ * value, nearest rounding toward zero.
+ *
+ * @param d The value to inspect
+ * @param flags Control/status flags
+ */
+static inline int64_t
+mc_dec128_to_int64_ex (mc_dec128 d, mc_dec128_flagset *flags)
+{
+ extern int64_t bid128_to_int64_int (mc_dec128 d, mc_dec128_flagset *);
+ mc_dec128_flagset zero_flags = {0};
+ return bid128_to_int64_int (d, flags ? flags : &zero_flags);
+}
+
+/**
+ * @brief Obtain the a 64-bit binary integer value for the given Decimal128
+ * value, nearest rounding toward zero.
+ *
+ * @param d The value to inspect
+ */
+static inline int64_t
+mc_dec128_to_int64 (mc_dec128 d)
+{
+ return mc_dec128_to_int64_ex (d, NULL);
+}
+
+/// Constants for inspection/deconstruction of Decimal128
+enum {
+ /// Least non-canonical combination bits value
+ MC_DEC128_COMBO_NONCANONICAL = 3 << 15,
+ /// Least combination value indicating infinity
+ MC_DEC128_COMBO_INFINITY = 0x1e << 12,
+ /// The greatest Decimal128 biased exponent
+ MC_DEC128_MAX_BIASED_EXPONENT = 6143 + 6144,
+ /// The exponent bias of Decimal128
+ MC_DEC128_EXPONENT_BIAS = 6143 + 33, // +33 to include the 34 decimal digits
+ /// Minimum exponent value (without bias)
+ MC_DEC_MIN_EXPONENT = -6143,
+ /// Maximum exponent value (without bias)
+ MC_DEC_MAX_EXPONENT = 6144,
+};
+
+/// Obtain the value of the combination bits of a decimal128
+static inline uint32_t
+mc_dec128_combination (mc_dec128 d)
+{
+ // Grab the high 64 bits:
+ uint64_t hi = d._words[MLIB_IS_LITTLE_ENDIAN ? 1 : 0];
+ // Sign is the 64th bit:
+ int signpos = 64 - 1;
+ // Combo is the next 16 bits:
+ int fieldpos = signpos - 17;
+ int fieldmask = (1 << 17) - 1;
+ return (uint32_t) ((hi >> fieldpos) & (uint32_t) fieldmask);
+}
+
+/**
+ * @brief Obtain the value of the high 49 bits of the Decimal128 coefficient
+ */
+static inline uint64_t
+mc_dec128_coeff_high (mc_dec128 d)
+{
+ uint64_t hi_field_mask = (1ull << 49) - 1;
+ uint32_t combo = mc_dec128_combination (d);
+ if (combo < MC_DEC128_COMBO_NONCANONICAL) {
+ uint64_t hi = d._words[MLIB_IS_LITTLE_ENDIAN ? 1 : 0];
+ return hi & hi_field_mask;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * @brief Obtain the value of the low 64 bits of the Decimal128 coefficient
+ */
+static inline uint64_t
+mc_dec128_coeff_low (mc_dec128 d)
+{
+ uint32_t combo = mc_dec128_combination (d);
+ if (combo < MC_DEC128_COMBO_NONCANONICAL) {
+ uint64_t lo = d._words[MLIB_IS_LITTLE_ENDIAN ? 0 : 1];
+ return lo;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * @brief Obtain the full coefficient of the given Decimal128 number. Requires a
+ * 128-bit integer.
+ */
+static inline mlib_int128
+mc_dec128_coeff (mc_dec128 d)
+{
+ // Hi bits
+ uint64_t hi = mc_dec128_coeff_high (d);
+ // Lo bits
+ uint64_t lo = mc_dec128_coeff_low (d);
+ // Shift and add
+ mlib_int128 hi_128 = mlib_int128_lshift (MLIB_INT128_CAST (hi), 64);
+ return mlib_int128_add (hi_128, MLIB_INT128_CAST (lo));
+}
+
+/**
+ * @brief Obtain the biased value of the Decimal128 exponent.
+ *
+ * The value is "biased" in that its binary value not actually zero for 10^0. It
+ * is offset by half the exponent range (the "bias") so it can encode the full
+ * positive and negative exponent range. The bias value is defined as
+ * MC_DEC128_EXPONENT_BIAS.
+ */
+static inline uint32_t
+mc_dec128_get_biased_exp (mc_dec128 d)
+{
+ uint32_t combo = mc_dec128_combination (d);
+ if (combo < MC_DEC128_COMBO_NONCANONICAL) {
+ return combo >> 3;
+ }
+ if (combo >= MC_DEC128_COMBO_INFINITY) {
+ return MC_DEC128_MAX_BIASED_EXPONENT + 1;
+ } else {
+ return (combo >> 1) & ((1 << 14) - 1);
+ }
+}
+
+/// Create a decimal string from a dec128 number. The result must be freed.
+static inline char *
+mc_dec128_to_new_decimal_string (mc_dec128 d)
+{
+ if (mc_dec128_is_zero (d)) {
+ // Just return "0"
+ char *s = (char *) calloc (2, 1);
+ if (s) {
+ s[0] = '0';
+ }
+ return s;
+ }
+
+ if (mc_dec128_is_negative (d)) {
+ // Negate the result, return a string with a '-' prefix
+ d = mc_dec128_negate (d);
+ char *s = mc_dec128_to_new_decimal_string (d);
+ if (!s) {
+ return NULL;
+ }
+ char *s1 = (char *) calloc (strlen (s) + 2, 1);
+ if (s1) {
+ s1[0] = '-';
+ strcpy (s1 + 1, s);
+ }
+ free (s);
+ return s1;
+ }
+
+ if (mc_dec128_is_inf (d) || mc_dec128_is_nan (d)) {
+ const char *r = mc_dec128_is_inf (d) ? "Infinity" : "NaN";
+ char *c = (char *) calloc (strlen (r) + 1, 1);
+ if (c) {
+ strcpy (c, r);
+ }
+ return c;
+ }
+
+ const char DIGITS[] = "0123456789";
+ const mc_dec128 TEN = MC_DEC128_C (10);
+
+ // Format the whole and fractional part separately.
+ mc_dec128_modf_result modf = mc_dec128_modf (d);
+
+ if (mc_dec128_is_zero (modf.frac)) {
+ // This is a non-zero integer
+ // Allocate enough digits:
+ mc_dec128 log10 = mc_dec128_modf (mc_dec128_log10 (d)).whole;
+ int64_t ndigits = mc_dec128_to_int64 (log10) + 1;
+ // +1 for null
+ char *strbuf = (char *) calloc ((size_t) (ndigits + 1), 1);
+ if (strbuf) {
+ // Write the string backwards:
+ char *optr = strbuf + ndigits - 1;
+ while (!mc_dec128_is_zero (modf.whole)) {
+ mc_dec128 rem = mc_dec128_fmod (modf.whole, TEN);
+ int64_t remi = mc_dec128_to_int64 (rem);
+ *optr-- = DIGITS[remi];
+ // Divide ten
+ modf = mc_dec128_modf (mc_dec128_div (modf.whole, TEN));
+ }
+ }
+ return strbuf;
+ } else if (mc_dec128_is_zero (modf.whole)) {
+ // This is only a fraction (less than one, but more than zero)
+ while (!mc_dec128_is_zero (mc_dec128_modf (d).frac)) {
+ d = mc_dec128_mul (d, TEN);
+ }
+ // 'd' is now a whole number
+ char *part = mc_dec128_to_new_decimal_string (d);
+ if (!part) {
+ return NULL;
+ }
+ char *buf = (char *) calloc (strlen (part) + 3, 1);
+ if (buf) {
+ buf[0] = '0';
+ buf[1] = '.';
+ strcpy (buf + 2, part);
+ }
+ free (part);
+ return buf;
+ } else {
+ // We have both a whole part and a fractional part
+ char *whole = mc_dec128_to_new_decimal_string (modf.whole);
+ if (!whole) {
+ return NULL;
+ }
+ char *frac = mc_dec128_to_new_decimal_string (modf.frac);
+ if (!frac) {
+ free (whole);
+ return NULL;
+ }
+ char *ret = (char *) calloc (strlen (whole) + strlen (frac) + 1, 1);
+ if (ret) {
+ char *out = ret;
+ strcpy (out, whole);
+ out += strlen (whole);
+ // "frac" contains a leading zero, which we don't want
+ strcpy (out, frac + 1);
+ }
+ free (whole);
+ free (frac);
+ return ret;
+ }
+}
+
+static inline mc_dec128
+mc_dec128_from_bson_iter (bson_iter_t *it)
+{
+ bson_decimal128_t b;
+ if (!bson_iter_decimal128 (it, &b)) {
+ mc_dec128 nan = MC_DEC128_POSITIVE_NAN;
+ return nan;
+ }
+ mc_dec128 ret;
+ memcpy (&ret, &b, sizeof b);
+ return ret;
+}
+
+static inline bson_decimal128_t
+mc_dec128_to_bson_decimal128 (mc_dec128 v)
+{
+ bson_decimal128_t ret;
+ memcpy (&ret, &v, sizeof ret);
+ return ret;
+}
+
+MLIB_C_LINKAGE_END
+
+#endif // MC_DEC128_H_INCLUDED