diff options
author | Richard <q@1bpm.net> | 2022-09-04 00:32:56 +0100 |
---|---|---|
committer | Richard <q@1bpm.net> | 2022-09-04 00:32:56 +0100 |
commit | 1d055261b4144dbf86b2658437015b15d4dd9bff (patch) | |
tree | 6049b19d1bf953a650383de1a5e438b8b82679f6 /include/jsoncons_ext/bson/bson_decimal128.hpp | |
download | csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.gz csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.bz2 csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.zip |
initial
Diffstat (limited to 'include/jsoncons_ext/bson/bson_decimal128.hpp')
-rw-r--r-- | include/jsoncons_ext/bson/bson_decimal128.hpp | 865 |
1 files changed, 865 insertions, 0 deletions
diff --git a/include/jsoncons_ext/bson/bson_decimal128.hpp b/include/jsoncons_ext/bson/bson_decimal128.hpp new file mode 100644 index 0000000..b487a04 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_decimal128.hpp @@ -0,0 +1,865 @@ +#ifndef JSONCONS_BSON_BSON_DECIMAL128_HPP +#define JSONCONS_BSON_BSON_DECIMAL128_HPP + +/* + * Implements decimal128_to_chars and decimal128_from_chars + * + * Based on the libjson functions bson_decimal128_to_string + * and bson_decimal128_from_string_w_len, available at + * https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-decimal128.h + * and https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-decimal128.c + * +*/ + +/* + * Copyright 2015 MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <string.h> +#include <cstring> +#include <ctype.h> +#include <system_error> +#include <algorithm> +#include <jsoncons/config/jsoncons_config.hpp> + +namespace jsoncons { namespace bson { + + struct decimal128_to_chars_result + { + char* ptr; + std::errc ec; + }; + + struct decimal128_from_chars_result + { + const char* ptr; + std::errc ec; + }; + +/** + * BSON_DECIMAL128_STRING: + * + * The length of a decimal128 string (with null terminator). + * + * 1 for the sign + * 35 for digits and radix + * 2 for exponent indicator and sign + * 4 for exponent digits + */ +#define BSON_DECIMAL128_STRING 43 + + struct TP1 + { + uint64_t low; + uint64_t high; + + constexpr TP1() : low(0), high(0) {} + constexpr TP1(uint64_t hi, uint64_t lo) : low(lo), high(hi) {} + }; + struct TP2 + { + uint64_t high; + uint64_t low; + + constexpr TP2() : high(0), low(0) {} + constexpr TP2(uint64_t hi, uint64_t lo) : high(hi), low(lo) {} + }; + + typedef std::conditional< + jsoncons::endian::native == jsoncons::endian::little, + TP1, + TP2 + >::type decimal128_t; + + inline + bool operator==(const decimal128_t& lhs, const decimal128_t& rhs) + { + return lhs.high == rhs.high && lhs.low == rhs.low; + } + + inline + bool operator!=(const decimal128_t& lhs, const decimal128_t& rhs) + { + return !(lhs == rhs); + } + + struct decimal128_limits + { + // The length of a decimal128 string (without null terminator). + // + // 1 for the sign + // 35 for digits and radix + // 2 for exponent indicator and sign + // 4 for exponent digits + static constexpr int buf_size = 42; + static constexpr int exponent_max = 6111; + static constexpr int exponent_min = -6176; + static constexpr int exponent_bias = 6176; + static constexpr int max_digits = 34; + + static constexpr decimal128_t nan() {return decimal128_t(0x7c00000000000000ull, 0);} + static constexpr decimal128_t infinity() {return decimal128_t(0x7800000000000000ull, 0);} + static constexpr decimal128_t neg_infinity() {return decimal128_t(0x7800000000000000ull + 0x8000000000000000ull, 0);} + }; + + inline + bool is_nan(decimal128_t dec) { return dec == decimal128_limits::nan(); } + + inline + bool is_inf(decimal128_t dec) { return dec == decimal128_limits::infinity(); } + + inline + bool is_neg_inf(decimal128_t dec) { return dec == decimal128_limits::neg_infinity(); } + + /** + * bson_uint128_t: + * + * This struct represents a 128 bit integer. + */ + typedef struct { + uint32_t parts[4]; /* 32-bit words stored high to low. */ + } bson_uint128_t; + + typedef struct { + uint64_t high, low; + } bson_uint128_6464_t; + + namespace detail { + + /** + *------------------------------------------------------------------------------ + * + * bson_uint128_divide1B -- + * + * This function divides a #bson_uint128_t by 1000000000 (1 billion) and + * computes the quotient and remainder. + * + * The remainder will contain 9 decimal digits for conversion to string. + * + * @value The #bson_uint128_t operand. + * @quotient A pointer to store the #bson_uint128_t quotient. + * @rem A pointer to store the #uint64_t remainder. + * + * Returns: + * The quotient at @quotient and the remainder at @rem. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + void bson_uint128_divide1B (bson_uint128_t value, /* IN */ + bson_uint128_t *quotient, /* OUT */ + uint32_t *rem) /* OUT */ + { + const uint32_t DIVISOR = 1000 * 1000 * 1000; + uint64_t _rem = 0; + int i = 0; + + if (!value.parts[0] && !value.parts[1] && !value.parts[2] && + !value.parts[3]) { + *quotient = value; + *rem = 0; + return; + } + + for (i = 0; i <= 3; i++) { + _rem <<= 32; /* Adjust remainder to match value of next dividend */ + _rem += value.parts[i]; /* Add the divided to _rem */ + value.parts[i] = (uint32_t) (_rem / DIVISOR); + _rem %= DIVISOR; /* Store the remainder */ + } + + *quotient = value; + *rem = (uint32_t) _rem; + } + + /** + *------------------------------------------------------------------------- + * + * mul64x64 -- + * + * This function multiplies two &uint64_t into a &bson_uint128_6464_t. + * + * Returns: + * The product of @left and @right. + * + * Side Effects: + * None. + * + *------------------------------------------------------------------------- + */ + + inline + void mul_64x64 (uint64_t left, /* IN */ + uint64_t right, /* IN */ + bson_uint128_6464_t *product) /* OUT */ + { + uint64_t left_high, left_low, right_high, right_low, product_high, + product_mid, product_mid2, product_low; + bson_uint128_6464_t rt = {0,0}; + + if (!left && !right) { + *product = rt; + return; + } + + left_high = left >> 32; + left_low = (uint32_t) left; + right_high = right >> 32; + right_low = (uint32_t) right; + + product_high = left_high * right_high; + product_mid = left_high * right_low; + product_mid2 = left_low * right_high; + product_low = left_low * right_low; + + product_high += product_mid >> 32; + product_mid = (uint32_t) product_mid + product_mid2 + (product_low >> 32); + + product_high = product_high + (product_mid >> 32); + product_low = (product_mid << 32) + (uint32_t) product_low; + + rt.high = product_high; + rt.low = product_low; + *product = rt; + } + + /** + *------------------------------------------------------------------------------ + * + * dec128_tolower -- + * + * This function converts the ASCII character @c to lowercase. It is locale + * insensitive (unlike the stdlib tolower). + * + * Returns: + * The lowercased character. + */ + + inline + char dec128_tolower (char c) + { + if (isupper (c)) { + c += 32; + } + + return c; + } + + /** + *------------------------------------------------------------------------------ + * + * dec128_istreq -- + * + * This function compares the null-terminated *ASCII* strings @a and @b + * for case-insensitive equality. + * + * Returns: + * true if the strings are equal, false otherwise. + */ + + inline + bool dec128_istreq (const char* a, const char* lasta, + const char* b, const char* lastb) + { + while (!(a == lasta && b == lastb)) + { + // strings are different lengths + if (a == lasta || b == lastb) + { + return false; + } + + if (dec128_tolower (*a) != dec128_tolower (*b)) { + return false; + } + + a++; + b++; + } + + return true; + } + + } // namespace detail + + + /** + *------------------------------------------------------------------------------ + * + * decimal128_to_chars -- + * + * This function converts a BID formatted decimal128 value to string, + * accepting a &decimal128_t as @dec. The string is stored at @str. + * + * @dec : The BID formatted decimal to convert. + * @str : The output decimal128 string. At least %BSON_DECIMAL128_STRING + *characters. + * + * Returns: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + decimal128_to_chars_result decimal128_to_chars(char* first, char* last, const decimal128_t& dec) + { + const std::string bson_decimal128_inf = "Infinity"; + const std::string bson_decimal128_nan = "NaN"; + + const uint32_t combination_mask = 0x1f; /* Extract least significant 5 bits */ + const uint32_t exponent_mask = 0x3fff; /* Extract least significant 14 bits */ + const uint32_t combination_infinity = 30; /* Value of combination field for Inf */ + const uint32_t combination_nan = 31; /* Value of combination field for NaN */ + const uint32_t exponent_bias = 6176; /* decimal128 exponent bias */ + + char* str_out = first; /* output pointer in string */ + char significand_str[35]; /* decoded significand digits */ + + /* Note: bits in this routine are referred to starting at 0, */ + /* from the sign bit, towards the coefficient. */ + uint32_t high; /* bits 0 - 31 */ + uint32_t midh; /* bits 32 - 63 */ + uint32_t midl; /* bits 64 - 95 */ + uint32_t low; /* bits 96 - 127 */ + uint32_t combination; /* bits 1 - 5 */ + uint32_t biased_exponent; /* decoded biased exponent (14 bits) */ + uint32_t significand_digits = 0; /* the number of significand digits */ + uint32_t significand[36] = {0}; /* the base-10 digits in the significand */ + uint32_t *significand_read = significand; /* read pointer into significand */ + int32_t exponent; /* unbiased exponent */ + int32_t scientific_exponent; /* the exponent if scientific notation is + * used */ + bool is_zero = false; /* true if the number is zero */ + + uint8_t significand_msb; /* the most signifcant significand bits (50-46) */ + bson_uint128_t + significand128; /* temporary storage for significand decoding */ + + memset (significand_str, 0, sizeof (significand_str)); + + if ((int64_t) dec.high < 0) { /* negative */ + *(str_out++) = '-'; + } + + low = (uint32_t) dec.low, midl = (uint32_t) (dec.low >> 32), + midh = (uint32_t) dec.high, high = (uint32_t) (dec.high >> 32); + + /* Decode combination field and exponent */ + combination = (high >> 26) & combination_mask; + + if (JSONCONS_UNLIKELY ((combination >> 3) == 3)) { + /* Check for 'special' values */ + if (combination == combination_infinity) { /* Infinity */ + if (last-str_out >= static_cast<ptrdiff_t >(bson_decimal128_inf.size())) + { + std::memcpy(str_out, bson_decimal128_inf.data(), bson_decimal128_inf.size()); + str_out += bson_decimal128_inf.size(); + } + *str_out = 0; + //strcpy_s (str_out, last-str_out, bson_decimal128_inf.c_str()); + return decimal128_to_chars_result{str_out, std::errc()}; + } else if (combination == combination_nan) { /* NaN */ + /* first, not str_out, to erase the sign */ + str_out = first; + if (last-str_out >= static_cast<ptrdiff_t >(bson_decimal128_nan.size())) + { + std::memcpy(str_out, bson_decimal128_nan.data(), bson_decimal128_nan.size()); + str_out += bson_decimal128_nan.size(); + } + *str_out = 0; + //strcpy_s (first, last-first, bson_decimal128_nan.c_str()); + /* we don't care about the NaN payload. */ + return decimal128_to_chars_result{str_out, std::errc()}; + } else { + biased_exponent = (high >> 15) & exponent_mask; + significand_msb = 0x8 + ((high >> 14) & 0x1); + } + } else { + significand_msb = (high >> 14) & 0x7; + biased_exponent = (high >> 17) & exponent_mask; + } + + exponent = biased_exponent - exponent_bias; + /* Create string of significand digits */ + + /* Convert the 114-bit binary number represented by */ + /* (high, midh, midl, low) to at most 34 decimal */ + /* digits through modulo and division. */ + significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xf) << 14); + significand128.parts[1] = midh; + significand128.parts[2] = midl; + significand128.parts[3] = low; + + if (significand128.parts[0] == 0 && significand128.parts[1] == 0 && + significand128.parts[2] == 0 && significand128.parts[3] == 0) { + is_zero = true; + } else if (significand128.parts[0] >= (1 << 17)) { + /* The significand is non-canonical or zero. + * In order to preserve compatibility with the densely packed decimal + * format, the maximum value for the significand of decimal128 is + * 1e34 - 1. If the value is greater than 1e34 - 1, the IEEE 754 + * standard dictates that the significand is interpreted as zero. + */ + is_zero = true; + } else { + for (int k = 3; k >= 0; k--) { + uint32_t least_digits = 0; + detail::bson_uint128_divide1B ( + significand128, &significand128, &least_digits); + + /* We now have the 9 least significant digits (in base 2). */ + /* Convert and output to string. */ + if (!least_digits) { + continue; + } + + for (int j = 8; j >= 0; j--) { + significand[k * 9 + j] = least_digits % 10; + least_digits /= 10; + } + } + } + + /* Output format options: */ + /* Scientific - [-]d.dddE(+/-)dd or [-]dE(+/-)dd */ + /* Regular - ddd.ddd */ + + if (is_zero) { + significand_digits = 1; + *significand_read = 0; + } else { + significand_digits = 36; + while (!(*significand_read)) { + significand_digits--; + significand_read++; + } + } + + scientific_exponent = significand_digits - 1 + exponent; + + /* The scientific exponent checks are dictated by the string conversion + * specification and are somewhat arbitrary cutoffs. + * + * We must check exponent > 0, because if this is the case, the number + * has trailing zeros. However, we *cannot* output these trailing zeros, + * because doing so would change the precision of the value, and would + * change stored data if the string converted number is round tripped. + */ + if (scientific_exponent < -6 || exponent > 0) { + /* Scientific format */ + *(str_out++) = char(*(significand_read++)) + '0'; + significand_digits--; + + if (significand_digits) { + *(str_out++) = '.'; + } + + for (std::size_t i = 0; i < significand_digits && (str_out - first) < 36; i++) { + *(str_out++) = char(*(significand_read++)) + '0'; + } + /* Exponent */ + *(str_out++) = 'E'; + + std::string s; + if (scientific_exponent >= 0) { + s.push_back('+'); + } + jsoncons::detail::from_integer(scientific_exponent, s); + if (str_out + s.size() < last) + { + std::memcpy(str_out, s.data(), s.size()); + } + else + { + return decimal128_to_chars_result{str_out, std::errc::value_too_large}; + } + str_out += s.size(); + } else { + /* Regular format with no decimal place */ + if (exponent >= 0) { + for (std::size_t i = 0; i < significand_digits && (str_out - first) < 36; i++) { + *(str_out++) = char(*(significand_read++)) + '0'; + } + } else { + int32_t radix_position = significand_digits + exponent; + + if (radix_position > 0) { /* non-zero digits before radix */ + for (int32_t i = 0; + i < radix_position && (str_out < last); + i++) { + *(str_out++) = char(*(significand_read++)) + '0'; + } + } else { /* leading zero before radix point */ + *(str_out++) = '0'; + } + + *(str_out++) = '.'; + while (radix_position++ < 0) { /* add leading zeros after radix */ + *(str_out++) = '0'; + } + + for (std::size_t i = 0; + (i < significand_digits - (std::max) (radix_position - 1, 0)) && + (str_out < last); + i++) { + *(str_out++) = char(*(significand_read++)) + '0'; + } + } + } + return decimal128_to_chars_result{str_out, std::errc()}; + } + + + + /** + *------------------------------------------------------------------------------ + * + * bson_decimal128_from_string_w_len -- + * + * This function converts @string in the format [+-]ddd[.]ddd[E][+-]dddd to + * decimal128. Out of range values are converted to +/-Infinity. Invalid + * strings are converted to NaN. @len is the length of the string, or -1 + * meaning the string is null-terminated. + * + * If more digits are provided than the available precision allows, + * round to the nearest expressable decimal128 with ties going to even will + * occur. + * + * Note: @string must be ASCII only! + * + * Returns: + * true on success, or false on failure. @dec will be NaN if @str was invalid + * The &decimal128_t converted from @string at @dec. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + + inline + decimal128_from_chars_result decimal128_from_chars(const char* first, const char* last, decimal128_t& dec) + { + const string_view inf_str = "inf"; + const string_view infinity_str = "infinity"; + const string_view nan_str = "nan"; + + ptrdiff_t len = last - first; + + bson_uint128_6464_t significand = {0,0}; + + const char* str_read = first; /* Read pointer for consuming str. */ + + /* Parsing state tracking */ + bool is_negative = false; + bool saw_radix = false; + bool includes_sign = false; /* True if the input first contains a sign. */ + bool found_nonzero = false; + + size_t significant_digits = 0; /* Total number of significant digits + * (no leading or trailing zero) */ + size_t ndigits_read = 0; /* Total number of significand digits read */ + size_t ndigits = 0; /* Total number of digits (no leading zeros) */ + size_t radix_position = 0; /* The number of the digits after radix */ + size_t first_nonzero = 0; /* The index of the first non-zero in *str* */ + + uint16_t digits[decimal128_limits::max_digits] = {0}; + uint16_t ndigits_stored = 0; /* The number of digits in digits */ + uint16_t *digits_insert = digits; /* Insertion pointer for digits */ + size_t first_digit = 0; /* The index of the first non-zero digit */ + size_t last_digit = 0; /* The index of the last digit */ + + int32_t exponent = 0; + uint64_t significand_high = 0; /* The high 17 digits of the significand */ + uint64_t significand_low = 0; /* The low 17 digits of the significand */ + uint16_t biased_exponent = 0; /* The biased exponent */ + + dec.high = 0; + dec.low = 0; + + if (*str_read == '+' || *str_read == '-') { + is_negative = *(str_read++) == '-'; + includes_sign = true; + } + + /* Check for Infinity or NaN */ + if (!isdigit (*str_read) && *str_read != '.') { + if (detail::dec128_istreq (str_read, last, inf_str.data(), inf_str.data()+inf_str.length()) || + detail::dec128_istreq (str_read, last, infinity_str.data(), infinity_str.data()+infinity_str.length())) + { + dec = is_negative ? decimal128_limits::neg_infinity() : decimal128_limits::infinity(); + return decimal128_from_chars_result{str_read,std::errc()}; + } else if (detail::dec128_istreq (str_read, last, nan_str.data(), nan_str.data()+nan_str.length())) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc()}; + } + + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + /* Read digits */ + while (((isdigit (*str_read) || *str_read == '.')) && + (len == -1 || str_read < first + len)) { + if (*str_read == '.') { + if (saw_radix) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + saw_radix = true; + str_read++; + continue; + } + + if (ndigits_stored < 34) { + if (*str_read != '0' || found_nonzero) { + if (!found_nonzero) { + first_nonzero = ndigits_read; + } + + found_nonzero = true; + *(digits_insert++) = *(str_read) - '0'; /* Only store 34 digits */ + ndigits_stored++; + } + } + + if (found_nonzero) { + ndigits++; + } + + if (saw_radix) { + radix_position++; + } + + ndigits_read++; + str_read++; + } + + if (saw_radix && !ndigits_read) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + /* Read exponent if exists */ + if (*str_read == 'e' || *str_read == 'E') { + ++str_read; + if (*str_read == '+') { + ++str_read; + } + auto result = jsoncons::detail::to_integer(str_read, last - str_read, exponent); + if (result.ec != jsoncons::detail::to_integer_errc()) + { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + str_read = result.ptr; + } + + if ((len == -1 || str_read < first + len) && *str_read) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + /* Done reading input. */ + /* Find first non-zero digit in digits */ + first_digit = 0; + + if (!ndigits_stored) { /* value is zero */ + first_digit = 0; + last_digit = 0; + digits[0] = 0; + ndigits = 1; + ndigits_stored = 1; + significant_digits = 0; + } else { + last_digit = ndigits_stored - 1; + significant_digits = ndigits; + /* Mark trailing zeros as non-significant */ + while (first[first_nonzero + significant_digits - 1 + includes_sign + + saw_radix] == '0') { + significant_digits--; + } + } + + + /* Normalization of exponent */ + /* Correct exponent based on radix position, and shift significand as needed + */ + /* to represent user input */ + + /* Overflow prevention */ + if (exponent <= static_cast<int32_t>(radix_position) && static_cast<int32_t>(radix_position) - exponent > (1 << 14)) { + exponent = decimal128_limits::exponent_min; + } else { + exponent -= static_cast<int32_t>(radix_position); + } + + /* Attempt to normalize the exponent */ + while (exponent > decimal128_limits::exponent_max) { + /* Shift exponent to significand and decrease */ + last_digit++; + + if (last_digit - first_digit > decimal128_limits::max_digits) { + /* The exponent is too great to shift into the significand. */ + if (significant_digits == 0) { + /* Value is zero, we are allowed to clamp the exponent. */ + exponent = decimal128_limits::exponent_max; + break; + } + + /* Overflow is not permitted, error. */ + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + exponent--; + } + + while (exponent < decimal128_limits::exponent_min || ndigits_stored < ndigits) { + /* Shift last digit */ + if (last_digit == 0) { + /* underflow is not allowed, but zero clamping is */ + if (significant_digits == 0) { + exponent = decimal128_limits::exponent_min; + break; + } + + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + if (ndigits_stored < ndigits) { + if (first[ndigits - 1 + includes_sign + saw_radix] - '0' != 0 && + significant_digits != 0) { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + ndigits--; /* adjust to match digits not stored */ + } else { + if (digits[last_digit] != 0) { + /* Inexact rounding is not allowed. */ + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + + + last_digit--; /* adjust to round */ + } + + if (exponent < decimal128_limits::exponent_max) { + exponent++; + } else { + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + } + + /* Round */ + /* We've normalized the exponent, but might still need to round. */ + if (last_digit - first_digit + 1 < significant_digits) { + uint8_t round_digit; + + /* There are non-zero digits after last_digit that need rounding. */ + /* We round to nearest, ties to even */ + round_digit = + first[first_nonzero + last_digit + includes_sign + saw_radix + 1] - + '0'; + + if (round_digit != 0) { + /* Inexact (non-zero) rounding is not allowed */ + dec = decimal128_limits::nan(); + return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; + } + } + + /* Encode significand */ + significand_high = 0, /* The high 17 digits of the significand */ + significand_low = 0; /* The low 17 digits of the significand */ + + if (significant_digits == 0) { /* read a zero */ + significand_high = 0; + significand_low = 0; + } else if (last_digit - first_digit < 17) { + size_t d_idx = first_digit; + significand_low = digits[d_idx++]; + + for (; d_idx <= last_digit; d_idx++) { + significand_low *= 10; + significand_low += digits[d_idx]; + significand_high = 0; + } + } else { + size_t d_idx = first_digit; + significand_high = digits[d_idx++]; + + for (; d_idx <= last_digit - 17; d_idx++) { + significand_high *= 10; + significand_high += digits[d_idx]; + } + + significand_low = digits[d_idx++]; + + for (; d_idx <= last_digit; d_idx++) { + significand_low *= 10; + significand_low += digits[d_idx]; + } + } + + detail::mul_64x64 (significand_high, 100000000000000000ull, &significand); + significand.low += significand_low; + + if (significand.low < significand_low) { + significand.high += 1; + } + + + biased_exponent = static_cast<uint16_t>(exponent + static_cast<int32_t>(decimal128_limits::exponent_bias)); + + /* Encode combination, exponent, and significand. */ + if ((significand.high >> 49) & 1) { + /* Encode '11' into bits 1 to 3 */ + dec.high |= (0x3ull << 61); + dec.high |= (biased_exponent & 0x3fffull) << 47; + dec.high |= significand.high & 0x7fffffffffffull; + } else { + dec.high |= (biased_exponent & 0x3fffull) << 49; + dec.high |= significand.high & 0x1ffffffffffffull; + } + + dec.low = significand.low; + + /* Encode sign */ + if (is_negative) { + dec.high |= 0x8000000000000000ull; + } + + return decimal128_from_chars_result{str_read,std::errc()}; + } + +} // namespace bson +} // namespace jsoncons + +#endif |