LCOV - code coverage report
Current view: top level - src - decimal_to_double.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 89 89 100.0 %
Date: 2026-07-14 21:28:07 Functions: 6 6 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 49 56 87.5 %

           Branch data     Line data    Source code
       1                 :            : // Copyright 2019-2026 David Robillard <d@drobilla.net>
       2                 :            : // SPDX-License-Identifier: ISC
       3                 :            : 
       4                 :            : #include <exess/exess.h>
       5                 :            : 
       6                 :            : #include "big_decimal.h"
       7                 :            : #include "bigint.h"
       8                 :            : #include "decimal_to_double.h"
       9                 :            : #include "ieee_float.h"
      10                 :            : #include "int_math.h"
      11                 :            : #include "macros.h"
      12                 :            : #include "soft_float.h"
      13                 :            : 
      14                 :            : #include <assert.h>
      15                 :            : #include <float.h>
      16                 :            : #include <math.h>
      17                 :            : #include <stdbool.h>
      18                 :            : #include <stdint.h>
      19                 :            : #include <string.h>
      20                 :            : 
      21                 :            : /// Number of digits always represented exactly by an unsigned 64-bit integer
      22                 :            : static const int uint64_digits10 = 19;
      23                 :            : 
      24                 :            : static EXESS_NONBLOCKING uint64_t
      25                 :      47260 : normalize(ExessSoftFloat* value, const uint64_t error)
      26                 :            : {
      27                 :      47260 :   const int original_e = value->e;
      28                 :            : 
      29                 :      47260 :   *value = soft_float_normalize(*value);
      30                 :            : 
      31         [ -  + ]:      47260 :   assert(value->e <= original_e);
      32                 :      47260 :   return error << (unsigned)(original_e - value->e);
      33                 :            : }
      34                 :            : 
      35                 :            : /**
      36                 :            :    Return the error added by floating point multiplication.
      37                 :            : 
      38                 :            :    Should be l + r + l*r/(2^64) + 0.5, but we short the denominator to 63 due
      39                 :            :    to lack of precision, which effectively rounds up.
      40                 :            : */
      41                 :            : static inline uint64_t
      42                 :      23634 : product_error(const uint64_t lerror,
      43                 :            :               const uint64_t rerror,
      44                 :            :               const uint64_t half_ulp)
      45                 :            : {
      46                 :      23634 :   return lerror + rerror + ((lerror * rerror) >> 63U) + half_ulp;
      47                 :            : }
      48                 :            : 
      49                 :            : /**
      50                 :            :    Guess the binary floating point value for decimal input.
      51                 :            : 
      52                 :            :    @param significand Significand from the input.
      53                 :            :    @param expt10 Decimal exponent from the input.
      54                 :            :    @param n_digits Number of decimal digits in the significand.
      55                 :            :    @param[out] guess Either the exact number, or its predecessor.
      56                 :            :    @return True if `guess` is correct.
      57                 :            : */
      58                 :            : static EXESS_I_NONBLOCKING bool
      59                 :      23630 : sftod(const uint64_t        significand,
      60                 :            :       const int             expt10,
      61                 :            :       const int             n_digits,
      62                 :            :       ExessSoftFloat* const guess)
      63                 :            : {
      64         [ -  + ]:      23630 :   assert(expt10 <= max_dec_expt);
      65         [ -  + ]:      23630 :   assert(expt10 >= min_dec_expt);
      66                 :            : 
      67                 :            :   /* The general idea here is to try and find a power of 10 that we can
      68                 :            :      multiply by the significand to get the number.  We get one from the
      69                 :            :      cache which is possibly too small, then multiply by another power of 10
      70                 :            :      to make up the difference if necessary.  For example, with a target
      71                 :            :      power of 10^70, if we get 10^68 from the cache, then we multiply again
      72                 :            :      by 10^2.  This, as well as normalization, accumulates error, which is
      73                 :            :      tracked throughout to know if we got the precise number. */
      74                 :            : 
      75                 :            :   // Use a common denominator of 2^3 to avoid fractions
      76                 :      23630 :   EXESS_CONSTEXPR unsigned lg_denom = 3;
      77                 :      23630 :   EXESS_CONSTEXPR uint64_t denom    = 1U << 3U;
      78                 :      23630 :   EXESS_CONSTEXPR uint64_t half_ulp = 4U;
      79                 :            : 
      80                 :            :   // Start out with just the significand, and no error
      81                 :      23630 :   ExessSoftFloat input = {significand, 0};
      82                 :      23630 :   uint64_t       error = normalize(&input, 0);
      83                 :            : 
      84                 :            :   // Get a power of 10 that takes us most of the way without overshooting
      85                 :      23630 :   int            cached_expt10 = 0;
      86                 :      23630 :   ExessSoftFloat pow10         = soft_float_pow10_under(expt10, &cached_expt10);
      87                 :            : 
      88                 :            :   // Get an exact fixup power if necessary
      89                 :      23630 :   const int d_expt10 = expt10 - cached_expt10;
      90         [ +  + ]:      23630 :   if (d_expt10) {
      91                 :      20553 :     input = soft_float_multiply(input, soft_float_exact_pow10(d_expt10));
      92         [ +  + ]:      20553 :     if (d_expt10 > uint64_digits10 - n_digits) {
      93                 :       9899 :       error += half_ulp; // Product does not fit in an integer
      94                 :            :     }
      95                 :            :   }
      96                 :            : 
      97                 :            :   // Multiply the significand by the power, normalize, and update the error
      98                 :      23630 :   input = soft_float_multiply(input, pow10);
      99                 :      23630 :   error = normalize(&input, product_error(error, half_ulp, half_ulp));
     100                 :            : 
     101                 :            :   // Get the effective number of significant bits from the order of magnitude
     102                 :      23630 :   const int magnitude = MIN(DBL_MANT_DIG, 64 + input.e - dbl_subnormal_expt);
     103                 :      23630 :   const unsigned n_significant_bits = (unsigned)MAX(0, magnitude);
     104                 :            : 
     105                 :            :   // Calculate the number of "extra" bits of precision we have
     106         [ -  + ]:      23630 :   assert(n_significant_bits <= 64);
     107                 :      23630 :   unsigned n_extra_bits = 64U - n_significant_bits;
     108         [ +  + ]:      23630 :   if (n_extra_bits + lg_denom >= 64U) {
     109                 :            :     // Very small subnormal where extra * denom does not fit in an integer
     110                 :            :     // Shift right (and accumulate some more error) to compensate
     111                 :          4 :     const unsigned amount = (n_extra_bits + lg_denom) - 63;
     112                 :            : 
     113                 :          4 :     input.f >>= amount;
     114                 :          4 :     input.e += (int)amount;
     115                 :          4 :     error = product_error((error >> amount) + 1U, half_ulp, half_ulp);
     116                 :          4 :     n_extra_bits -= amount;
     117                 :            :   }
     118                 :            : 
     119                 :            :   // Calculate boundaries for the extra bits (with the common denominator)
     120         [ -  + ]:      23630 :   assert(n_extra_bits < 64);
     121                 :      23630 :   const uint64_t extra_mask = (1ULL << n_extra_bits) - 1U;
     122                 :      23630 :   const uint64_t extra_bits = (input.f & extra_mask) * denom;
     123                 :      23630 :   const uint64_t middle     = (1ULL << (n_extra_bits - 1U)) * denom;
     124                 :      23630 :   const uint64_t low        = middle - error;
     125                 :      23630 :   const uint64_t high       = middle + error;
     126                 :            : 
     127                 :            :   // Round to nearest representable double
     128                 :      23630 :   guess->f = (input.f >> n_extra_bits) + (extra_bits >= high);
     129                 :      23630 :   guess->e = input.e + (int)n_extra_bits;
     130                 :            : 
     131                 :            :   // Too inaccurate if the extra bits are within the error around the middle
     132   [ +  +  +  + ]:      23630 :   return extra_bits <= low || extra_bits >= high;
     133                 :            : }
     134                 :            : 
     135                 :            : static EXESS_I_NONBLOCKING int
     136                 :         29 : compare_buffer(const char* buf, const int expt, const ExessSoftFloat upper)
     137                 :            : {
     138                 :            :   ExessBigint buf_bigint;
     139                 :         29 :   exess_bigint_set_decimal_string(&buf_bigint, buf);
     140                 :            : 
     141                 :            :   ExessBigint upper_bigint;
     142                 :         29 :   exess_bigint_set_u64(&upper_bigint, upper.f);
     143                 :            : 
     144         [ +  + ]:         29 :   if (expt >= 0) {
     145                 :         23 :     exess_bigint_multiply_pow10(&buf_bigint, (unsigned)expt);
     146                 :            :   } else {
     147                 :          6 :     exess_bigint_multiply_pow10(&upper_bigint, (unsigned)-expt);
     148                 :            :   }
     149                 :            : 
     150         [ +  + ]:         29 :   if (upper.e > 0) {
     151                 :         23 :     exess_bigint_shift_left(&upper_bigint, (unsigned)upper.e);
     152                 :            :   } else {
     153                 :          6 :     exess_bigint_shift_left(&buf_bigint, (unsigned)-upper.e);
     154                 :            :   }
     155                 :            : 
     156                 :         29 :   return exess_bigint_compare(&buf_bigint, &upper_bigint);
     157                 :            : }
     158                 :            : 
     159                 :            : EXESS_PURE_FUNC static uint64_t
     160                 :      30653 : read_fraction(size_t n_digits, const char* const digits)
     161                 :            : {
     162                 :      30653 :   uint64_t frac = 0;
     163                 :            : 
     164         [ +  + ]:     434850 :   for (unsigned i = 0U; i < n_digits; ++i) {
     165   [ +  -  -  + ]:     404197 :     assert(digits[i] >= '0' && digits[i] <= '9');
     166                 :     404197 :     frac = (frac * 10U) + (unsigned)(digits[i] - '0');
     167                 :            :   }
     168                 :            : 
     169                 :      30653 :   return frac;
     170                 :            : }
     171                 :            : 
     172                 :            : EXESS_I_NONBLOCKING double
     173                 :      31651 : decimal_to_double(const BigDecimal in, const char* const digits)
     174                 :            : {
     175                 :      31651 :   EXESS_CONSTEXPR int n_exact_pow10 = sizeof(POW10) / sizeof(POW10[0]);
     176                 :            : 
     177                 :      31651 :   EXESS_CONSTEXPR unsigned max_exact_int_digits = 15; // Digits that fit exactly
     178                 :      31651 :   EXESS_CONSTEXPR int      max_decimal_power    = 309;  // Max finite power
     179                 :      31651 :   EXESS_CONSTEXPR int      min_decimal_power    = -324; // Min non-zero power
     180                 :            : 
     181                 :      31651 :   EXESS_CONSTEXPR double special_values[] = {
     182                 :            :     (double)NAN, (double)-INFINITY, (double)INFINITY, -0.0, 0.0};
     183                 :            : 
     184         [ +  + ]:      31651 :   if (in.kind < EXESS_NEGATIVE) {
     185                 :        995 :     return special_values[in.kind]; // Special case
     186                 :            :   }
     187                 :            : 
     188         [ +  + ]:      30656 :   const int sign = in.kind == EXESS_POSITIVE ? 1 : -1;
     189                 :            : 
     190         [ +  + ]:      30656 :   if (in.expt > max_decimal_power) {
     191                 :          2 :     return sign * (double)INFINITY; // Infinite magnitude => infinity
     192                 :            :   }
     193                 :            : 
     194         [ +  + ]:      30654 :   if (in.expt < min_decimal_power) {
     195                 :          1 :     return sign * 0.0; // Effectively zero
     196                 :            :   }
     197                 :            : 
     198                 :            :   // Represent value with integer mantissa and adjusted decimal power
     199                 :      30653 :   const uint64_t frac  = read_fraction(in.n_digits, digits);
     200                 :      30653 :   const int      power = in.expt - (int)in.n_digits;
     201                 :            : 
     202         [ +  + ]:      30653 :   if (in.n_digits < max_exact_int_digits) { // Exact integer value
     203         [ +  + ]:      12777 :     if (power < 0) {
     204         [ +  + ]:       6681 :       if (-power < n_exact_pow10) {
     205                 :       3182 :         return sign * ((double)frac / (double)POW10[-power]);
     206                 :            :       }
     207         [ +  + ]:       6096 :     } else if (power < n_exact_pow10) {
     208                 :       3841 :       return sign * ((double)frac * (double)POW10[power]);
     209                 :            :     }
     210                 :            :   }
     211                 :            : 
     212                 :            :   // Try to guess the number using only soft floating point (fast path)
     213                 :      23630 :   ExessSoftFloat guess = {0, 0};
     214                 :      23630 :   const bool     exact = sftod(frac, power, (int)in.n_digits, &guess);
     215                 :      23630 :   const double   g     = soft_float_to_double(guess);
     216         [ +  + ]:      23630 :   if (exact) {
     217                 :      23601 :     return sign * g;
     218                 :            :   }
     219                 :            : 
     220                 :            :   // Not sure, guess is either the number or its predecessor (rare slow path)
     221                 :            :   // Compare it with the buffer using bigints to find out which
     222                 :         29 :   const ExessSoftFloat upper = {(guess.f * 2) + 1, guess.e - 1};
     223                 :         29 :   const int            cmp   = compare_buffer(digits, power, upper);
     224   [ +  +  +  +  :         29 :   const bool round_up        = (cmp > 0) || (cmp == 0 && (guess.f & 1U) != 0);
                   +  + ]
     225                 :            : 
     226         [ +  + ]:         29 :   return sign * (round_up ? nextafter(g, (double)INFINITY) : g);
     227                 :            : }

Generated by: LCOV version 1.16