BSON-XS

 view release on metacpan or  search on metacpan

bson/bson-decimal128.c  view on Meta::CPAN

 * Side effects:
 *    None.
 *
 *------------------------------------------------------------------------------
 */
static 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 = _rem;
}


/**
 *------------------------------------------------------------------------------
 *
 * bson_decimal128_to_string --
 *
 *    This function converts a BID formatted decimal128 value to string,
 *    accepting a &bson_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.
 *
 *------------------------------------------------------------------------------
 */
void
bson_decimal128_to_string (const bson_decimal128_t *dec,        /* IN  */
                           char                    *str)        /* OUT */
{
   uint32_t COMBINATION_MASK = 0x1f;   /* Extract least significant 5 bits */
   uint32_t EXPONENT_MASK = 0x3fff;    /* Extract least significant 14 bits */
   uint32_t COMBINATION_INFINITY = 30; /* Value of combination field for Inf */
   uint32_t COMBINATION_NAN = 31;      /* Value of combination field for NaN */
   uint32_t EXPONENT_BIAS = 6176;      /* decimal128 exponent bias */

   char *str_out = str;                /* 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 */
   size_t i;                         /* indexing variables */
   int j, k;

   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 (BSON_UNLIKELY ((combination >> 3) == 3)) {
      /* Check for 'special' values */
      if (combination == COMBINATION_INFINITY) {  /* Infinity */
         strcpy (str_out, "Inf");
         return;
      } else if (combination == COMBINATION_NAN) { /* NaN */
         /* str, not str_out, to erase the sign */
         strcpy (str, "NaN");
         /* we don't care about the NaN payload. */
         return;
      } 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 compatability 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 (k = 3; k >= 0; k--) {
         uint32_t least_digits = 0;
         _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 (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--;

bson/bson-decimal128.c  view on Meta::CPAN

/**
 *------------------------------------------------------------------------------
 *
 * _dec128_tolower --
 *
 *    This function converts the ASCII character @c to lowercase.  It is locale
 *    insensitive (unlike the stdlib tolower).
 *
 * Returns:
 *    The lowercased character.
 */
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.
 */
bool
_dec128_istreq (const char *a, /* IN */
                const char *b /* IN */)
{
   while (*a != '\0' || *b != '\0') {
      /* strings are different lengths. */
      if (*a == '\0' || *b == '\0') {
         return false;
      }

      if (_dec128_tolower (*a) != _dec128_tolower (*b)) {
         return false;
      }

      a++;
      b++;
   }

   return true;
}

/**
 *------------------------------------------------------------------------------
 *
 * bson_decimal128_from_string --
 *
 *    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.
 *
 *    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 &bson_decimal128_t converted from @string at @dec.
 *
 * Side effects:
 *    None.
 *
 *------------------------------------------------------------------------------
 */
bool
bson_decimal128_from_string (const char        *string, /* IN */
                             bson_decimal128_t *dec) /* OUT */
{
   _bson_uint128_6464_t significand = { 0 };

   const char *str_read = string;  /* Read pointer for consuming str. */

   /* Parsing state tracking */
   bool is_negative = false;
   bool saw_radix = false;
   bool includes_sign = false;  /* True if the input string 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[BSON_DECIMAL128_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;
   size_t i = 0;                   /* loop index over array */
   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 */

   BSON_ASSERT (dec);
   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 (_dec128_istreq (str_read, "inf") ||
          _dec128_istreq (str_read, "infinity")) {
         BSON_DECIMAL128_SET_INF (*dec, is_negative);
         return true;
      } else if (_dec128_istreq (str_read, "nan")) {
         BSON_DECIMAL128_SET_NAN (*dec);
         return true;
      }

      BSON_DECIMAL128_SET_NAN (*dec);
      return false;
   }

   /* Read digits */
   while (isdigit (*str_read) || *str_read == '.') {
      if (*str_read == '.') {
         if (saw_radix) {
            BSON_DECIMAL128_SET_NAN (*dec);
            return false;
         }

         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) {
      BSON_DECIMAL128_SET_NAN (*dec);
      return false;
   }

   /* Read exponent if exists */
   if (*str_read == 'e' || *str_read == 'E') {
      int nread = 0;
#ifdef _MSC_VER



( run in 2.197 seconds using v1.01-cache-2.11-cpan-acf6aa7dc9e )