LCOV - code coverage report
Current view: top level - include/flatcc/portable - grisu3_print.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 47 82 57.3 %
Date: 2016-11-30 13:12:14 Functions: 4 5 80.0 %
Branches: 20 70 28.6 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2016 Mikkel F. Jørgensen, dvide.com
       3                 :            :  * Copyright author of MathGeoLib (https://github.com/juj)
       4                 :            :  *
       5                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       6                 :            :  * you may not use this file except in compliance with the License.
       7                 :            :  * You may obtain a copy of the License at
       8                 :            :  *
       9                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
      10                 :            :  *
      11                 :            :  * Unless required by applicable law or agreed to in writing, software
      12                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      13                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14                 :            :  * See the License for the specific language governing permissions and
      15                 :            :  * limitations under the License. http://www.apache.org/licenses/LICENSE-2.0
      16                 :            :  */
      17                 :            : 
      18                 :            : /*
      19                 :            :  * Extracted from MathGeoLib.
      20                 :            :  *
      21                 :            :  * mikkelfj:
      22                 :            :  * - Fixed final output when printing single digit negative exponent to
      23                 :            :  * have leading zero (important for JSON).
      24                 :            :  * - Changed formatting to prefer 0.012 over 1.2-e-2.
      25                 :            :  *
      26                 :            :  * Large portions of the original grisu3.c file has been moved to
      27                 :            :  * grisu3_math.h, the rest is placed here.
      28                 :            :  *
      29                 :            :  * See also comments in grisu3_math.h.
      30                 :            :  *
      31                 :            :  * MatGeoLib grisu3.c comment:
      32                 :            :  *
      33                 :            :  *     This file is part of an implementation of the "grisu3" double to string
      34                 :            :  *     conversion algorithm described in the research paper
      35                 :            :  *
      36                 :            :  *     "Printing Floating-Point Numbers Quickly And Accurately with Integers"
      37                 :            :  *     by Florian Loitsch, available at
      38                 :            :  *     http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
      39                 :            :  */
      40                 :            : 
      41                 :            : #ifndef GRISU3_PRINT_H
      42                 :            : #define GRISU3_PRINT_H
      43                 :            : 
      44                 :            : #include <stdio.h> // sprintf
      45                 :            : #include <assert.h> // assert
      46                 :            : 
      47                 :            : #include "grisu3_math.h"
      48                 :            : 
      49                 :            : /*
      50                 :            :  * The lightweight "portable" C library recognizes grisu3 support if
      51                 :            :  * included first.
      52                 :            :  */
      53                 :            : #define grisu3_print_double_is_defined 1
      54                 :            : 
      55                 :            : /*
      56                 :            :  * Not sure we have an exact definition, but we get up to 23
      57                 :            :  * emperically. There is some math ensuring it does not go awol though,
      58                 :            :  * like 18 digits + exponent or so.
      59                 :            :  * This max should be safe size buffer for printing, including zero term.
      60                 :            :  */
      61                 :            : #define GRISU3_PRINT_MAX 30
      62                 :            : 
      63                 :          4 : static int grisu3_round_weed(char *buffer, int len, uint64_t wp_W, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t ulp)
      64                 :            : {
      65                 :          4 :     uint64_t wp_Wup = wp_W - ulp;
      66                 :          4 :     uint64_t wp_Wdown = wp_W + ulp;
      67 [ -  + ][ #  # ]:          4 :     while(rest < wp_Wup && delta - rest >= ten_kappa
      68 [ #  # ][ #  # ]:          0 :         && (rest + ten_kappa < wp_Wup || wp_Wup - rest >= rest + ten_kappa - wp_Wup))
      69                 :            :     {
      70                 :          0 :         --buffer[len-1];
      71                 :            :         rest += ten_kappa;
      72                 :            :     }
      73 [ +  - ][ -  + ]:          4 :     if (rest < wp_Wdown && delta - rest >= ten_kappa
      74 [ #  # ][ #  # ]:          0 :         && (rest + ten_kappa < wp_Wdown || wp_Wdown - rest > rest + ten_kappa - wp_Wdown))
      75                 :            :         return 0;
      76                 :            : 
      77 [ +  - ][ -  + ]:          4 :     return 2*ulp <= rest && rest <= delta - 4*ulp;
      78                 :            : }
      79                 :            : 
      80                 :          8 : static int grisu3_digit_gen(grisu3_diy_fp_t low, grisu3_diy_fp_t w, grisu3_diy_fp_t high, char *buffer, int *length, int *kappa)
      81                 :            : {
      82                 :            :     uint64_t unit = 1;
      83                 :          4 :     grisu3_diy_fp_t too_low = { low.f - unit, low.e };
      84                 :          4 :     grisu3_diy_fp_t too_high = { high.f + unit, high.e };
      85                 :            :     grisu3_diy_fp_t unsafe_interval =  grisu3_diy_fp_minus(too_high, too_low);
      86                 :          4 :     grisu3_diy_fp_t one = { 1ULL << -w.e, w.e };
      87                 :          4 :     uint32_t p1 = (uint32_t)(too_high.f >> -one.e);
      88                 :          4 :     uint64_t p2 = too_high.f & (one.f - 1);
      89                 :            :     uint32_t div;
      90                 :          4 :     *kappa = grisu3_largest_pow10(p1, GRISU3_DIY_FP_FRACT_SIZE + one.e, &div);
      91                 :          4 :     *length = 0;
      92                 :            : 
      93         [ +  - ]:          4 :     while(*kappa > 0)
      94                 :            :     {
      95                 :            :         uint64_t rest;
      96                 :          4 :         int digit = p1 / div;
      97                 :          4 :         buffer[*length] = (char)('0' + digit);
      98                 :          4 :         ++*length;
      99                 :          4 :         p1 %= div;
     100                 :          4 :         --*kappa;
     101                 :          4 :         rest = ((uint64_t)p1 << -one.e) + p2;
     102         [ +  - ]:          4 :         if (rest < unsafe_interval.f) return grisu3_round_weed(buffer, *length, grisu3_diy_fp_minus(too_high, w).f, unsafe_interval.f, rest, (uint64_t)div << -one.e, unit);
     103                 :          0 :         div /= 10;
     104                 :            :     }
     105                 :            : 
     106                 :            :     for(;;)
     107                 :            :     {
     108                 :            :         int digit;
     109                 :          0 :         p2 *= 10;
     110                 :          0 :         unit *= 10;
     111                 :          0 :         unsafe_interval.f *= 10;
     112                 :            :         // Integer division by one.
     113                 :          0 :         digit = (int)(p2 >> -one.e);
     114                 :          0 :         buffer[*length] = (char)('0' + digit);
     115                 :          0 :         ++*length;
     116                 :          0 :         p2 &= one.f - 1;  // Modulo by one.
     117                 :          0 :         --*kappa;
     118         [ #  # ]:          0 :         if (p2 < unsafe_interval.f) return grisu3_round_weed(buffer, *length, grisu3_diy_fp_minus(too_high, w).f * unit, unsafe_interval.f, p2, one.f, unit);
     119                 :            :     }
     120                 :            : }
     121                 :            : 
     122                 :          4 : static int grisu3(double v, char *buffer, int *length, int *d_exp)
     123                 :            : {
     124                 :            :     int mk, kappa, success;
     125                 :            :     grisu3_diy_fp_t dfp = grisu3_cast_diy_fp_from_double(v);
     126                 :            :     grisu3_diy_fp_t w = grisu3_diy_fp_normalize(dfp);
     127                 :            : 
     128                 :            :     // normalize boundaries
     129                 :          4 :     grisu3_diy_fp_t t = { (dfp.f << 1) + 1, dfp.e - 1 };
     130                 :            :     grisu3_diy_fp_t b_plus = grisu3_diy_fp_normalize(t);
     131                 :            :     grisu3_diy_fp_t b_minus;
     132                 :            :     grisu3_diy_fp_t c_mk; // Cached power of ten: 10^-k
     133                 :            :     uint64_t u64 = grisu3_cast_uint64_from_double(v);
     134                 :            :     assert(v > 0 && v <= 1.7976931348623157e308); // Grisu only handles strictly positive finite numbers.
     135 [ +  + ][ +  - ]:          4 :     if (!(u64 & GRISU3_D64_FRACT_MASK) && (u64 & GRISU3_D64_EXP_MASK) != 0) { b_minus.f = (dfp.f << 2) - 1; b_minus.e =  dfp.e - 2;} // lower boundary is closer?
     136                 :          2 :     else { b_minus.f = (dfp.f << 1) - 1; b_minus.e = dfp.e - 1; }
     137                 :          4 :     b_minus.f = b_minus.f << (b_minus.e - b_plus.e);
     138                 :            :     b_minus.e = b_plus.e;
     139                 :            : 
     140                 :            :     mk = grisu3_diy_fp_cached_pow(GRISU3_MIN_TARGET_EXP - GRISU3_DIY_FP_FRACT_SIZE - w.e, &c_mk);
     141                 :            : 
     142                 :          4 :     w = grisu3_diy_fp_multiply(w, c_mk);
     143                 :          4 :     b_minus = grisu3_diy_fp_multiply(b_minus, c_mk);
     144                 :          4 :     b_plus  = grisu3_diy_fp_multiply(b_plus,  c_mk);
     145                 :            : 
     146                 :          4 :     success = grisu3_digit_gen(b_minus, w, b_plus, buffer, length, &kappa);
     147                 :          4 :     *d_exp = kappa - mk;
     148                 :          4 :     return success;
     149                 :            : }
     150                 :            : 
     151                 :          0 : static int grisu3_i_to_str(int val, char *str)
     152                 :            : {
     153                 :            :     int len, i;
     154                 :            :     char *s;
     155                 :            :     char *begin = str;
     156         [ #  # ]:          0 :     if (val < 0) { *str++ = '-'; val = -val; }
     157                 :            :     s = str;
     158                 :            : 
     159                 :            :     for(;;)
     160                 :            :     {
     161                 :          0 :         int ni = val / 10;
     162                 :          0 :         int digit = val - ni*10;
     163                 :          0 :         *s++ = (char)('0' + digit);
     164         [ #  # ]:          0 :         if (ni == 0)
     165                 :            :             break;
     166                 :            :         val = ni;
     167                 :            :     }
     168                 :          0 :     *s = '\0';
     169                 :          0 :     len = (int)(s - str);
     170         [ #  # ]:          0 :     for(i = 0; i < len/2; ++i)
     171                 :            :     {
     172                 :          0 :         char ch = str[i];
     173                 :          0 :         str[i] = str[len-1-i];
     174                 :          0 :         str[len-1-i] = ch;
     175                 :            :     }
     176                 :            : 
     177                 :          0 :     return (int)(s - begin);
     178                 :            : }
     179                 :            : 
     180                 :          4 : static int grisu3_print_double(double v, char *dst)
     181                 :            : {
     182                 :            :     int d_exp, len, success, decimals, i;
     183                 :            :     uint64_t u64 = grisu3_cast_uint64_from_double(v);
     184                 :            :     char *s2 = dst;
     185                 :            :     assert(dst);
     186                 :            : 
     187                 :            :     // Prehandle NaNs
     188         [ -  + ]:          4 :     if ((u64 << 1) > 0xFFE0000000000000ULL) return sprintf(dst, "NaN(%08X%08X)", (uint32_t)(u64 >> 32), (uint32_t)u64);
     189                 :            :     // Prehandle negative values.
     190         [ -  + ]:          4 :     if ((u64 & GRISU3_D64_SIGN) != 0) { *s2++ = '-'; v = -v; u64 ^= GRISU3_D64_SIGN; }
     191                 :            :     // Prehandle zero.
     192         [ -  + ]:          4 :     if (!u64) { *s2++ = '0'; *s2 = '\0'; return (int)(s2 - dst); }
     193                 :            :     // Prehandle infinity.
     194         [ -  + ]:          4 :     if (u64 == GRISU3_D64_EXP_MASK) { *s2++ = 'i'; *s2++ = 'n'; *s2++ = 'f'; *s2 = '\0'; return (int)(s2 - dst); }
     195                 :            : 
     196                 :          4 :     success = grisu3(v, s2, &len, &d_exp);
     197                 :            :     // If grisu3 was not able to convert the number to a string, then use old sprintf (suboptimal).
     198         [ -  + ]:          4 :     if (!success) return sprintf(s2, "%.17g", v) + (int)(s2 - dst);
     199                 :            : 
     200                 :            :     // We now have an integer string of form "151324135" and a base-10 exponent for that number.
     201                 :            :     // Next, decide the best presentation for that string by whether to use a decimal point, or the scientific exponent notation 'e'.
     202                 :            :     // We don't pick the absolute shortest representation, but pick a balance between readability and shortness, e.g.
     203                 :            :     // 1.545056189557677e-308 could be represented in a shorter form
     204                 :            :     // 1545056189557677e-323 but that would be somewhat unreadable.
     205         [ -  + ]:          4 :     decimals = GRISU3_MIN(-d_exp, GRISU3_MAX(1, len-1));
     206                 :            : 
     207                 :            :     // mikkelfj:
     208                 :            :     // fix zero prefix .1 => 0.1, important for JSON export.
     209                 :            :     // prefer unscientific notation at same length:
     210                 :            :     // -1.2345e-4 over -1.00012345,
     211                 :            :     // -1.0012345 over -1.2345e-3
     212 [ -  + ][ #  # ]:          4 :     if (d_exp < 0 && (len + d_exp) > -3 && len <= -d_exp)
                 [ #  # ]
     213                 :            :     {
     214                 :            :         // mikkelfj: fix zero prefix .1 => 0.1, and short exponents 1.3e-2 => 0.013.
     215                 :          0 :         memmove(s2 + 2 - d_exp - len, s2, len);
     216                 :          0 :         s2[0] = '0';
     217                 :          0 :         s2[1] = '.';
     218         [ #  # ]:          0 :         for (i = 2; i < 2-d_exp-len; ++i) s2[i] = '0';
     219                 :          0 :         len += i;
     220                 :            :     }
     221 [ -  + ][ #  # ]:          4 :     else if (d_exp < 0 && len > 1) // Add decimal point?
     222                 :            :     {
     223         [ #  # ]:          0 :         for(i = 0; i < decimals; ++i) s2[len-i] = s2[len-i-1];
     224                 :          0 :         s2[len++ - decimals] = '.';
     225                 :          0 :         d_exp += decimals;
     226                 :            :         // Need scientific notation as well?
     227         [ #  # ]:          0 :         if (d_exp != 0) { s2[len++] = 'e'; len += grisu3_i_to_str(d_exp, s2+len); }
     228                 :            :     }
     229                 :            :     // Add scientific notation?
     230         [ -  + ]:          4 :     else if (d_exp < 0 || d_exp > 2) { s2[len++] = 'e'; len += grisu3_i_to_str(d_exp, s2+len); }
     231                 :            :     // Add zeroes instead of scientific notation?
     232 [ -  + ][ #  # ]:          4 :     else if (d_exp > 0) { while(d_exp-- > 0) s2[len++] = '0'; }
     233                 :          4 :     s2[len] = '\0'; // grisu3 doesn't null terminate, so ensure termination.
     234                 :          4 :     return (int)(s2+len-dst);
     235                 :            : }
     236                 :            : 
     237                 :            : #endif /* GRISU3_PRINT_H */

Generated by: LCOV version 1.12