LCOV - code coverage report
Current view: top level - py - formatfloat.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.23.0-410-g82e69df33.info Lines: 172 172 100.0 %
Date: 2024-10-10 13:49:24 Functions: 1 1 100.0 %
Branches: 120 126 95.2 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * This file is part of the MicroPython project, http://micropython.org/
       3                 :            :  *
       4                 :            :  * The MIT License (MIT)
       5                 :            :  *
       6                 :            :  * Copyright (c) 2013, 2014 Damien P. George
       7                 :            :  *
       8                 :            :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       9                 :            :  * of this software and associated documentation files (the "Software"), to deal
      10                 :            :  * in the Software without restriction, including without limitation the rights
      11                 :            :  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      12                 :            :  * copies of the Software, and to permit persons to whom the Software is
      13                 :            :  * furnished to do so, subject to the following conditions:
      14                 :            :  *
      15                 :            :  * The above copyright notice and this permission notice shall be included in
      16                 :            :  * all copies or substantial portions of the Software.
      17                 :            :  *
      18                 :            :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      19                 :            :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      20                 :            :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      21                 :            :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      22                 :            :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      23                 :            :  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      24                 :            :  * THE SOFTWARE.
      25                 :            :  */
      26                 :            : 
      27                 :            : #include "py/mpconfig.h"
      28                 :            : #include "py/misc.h"
      29                 :            : #if MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE
      30                 :            : 
      31                 :            : #include <assert.h>
      32                 :            : #include <stdlib.h>
      33                 :            : #include <stdint.h>
      34                 :            : #include <math.h>
      35                 :            : #include "py/formatfloat.h"
      36                 :            : 
      37                 :            : /***********************************************************************
      38                 :            : 
      39                 :            :   Routine for converting a arbitrary floating
      40                 :            :   point number into a string.
      41                 :            : 
      42                 :            :   The code in this function was inspired from Fred Bayer's pdouble.c.
      43                 :            :   Since pdouble.c was released as Public Domain, I'm releasing this
      44                 :            :   code as public domain as well.
      45                 :            : 
      46                 :            :   The original code can be found in https://github.com/dhylands/format-float
      47                 :            : 
      48                 :            :   Dave Hylands
      49                 :            : 
      50                 :            : ***********************************************************************/
      51                 :            : 
      52                 :            : #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
      53                 :            : // 1 sign bit, 8 exponent bits, and 23 mantissa bits.
      54                 :            : // exponent values 0 and 255 are reserved, exponent can be 1 to 254.
      55                 :            : // exponent is stored with a bias of 127.
      56                 :            : // The min and max floats are on the order of 1x10^37 and 1x10^-37
      57                 :            : 
      58                 :            : #define FPTYPE float
      59                 :            : #define FPCONST(x) x##F
      60                 :            : #define FPROUND_TO_ONE 0.9999995F
      61                 :            : #define FPDECEXP 32
      62                 :            : #define FPMIN_BUF_SIZE 6 // +9e+99
      63                 :            : 
      64                 :            : #define FLT_SIGN_MASK   0x80000000
      65                 :            : 
      66                 :            : static inline int fp_signbit(float x) {
      67                 :            :     mp_float_union_t fb = {x};
      68                 :            :     return fb.i & FLT_SIGN_MASK;
      69                 :            : }
      70                 :            : #define fp_isnan(x) isnan(x)
      71                 :            : #define fp_isinf(x) isinf(x)
      72                 :            : static inline int fp_iszero(float x) {
      73                 :            :     mp_float_union_t fb = {x};
      74                 :            :     return fb.i == 0;
      75                 :            : }
      76                 :            : static inline int fp_isless1(float x) {
      77                 :            :     mp_float_union_t fb = {x};
      78                 :            :     return fb.i < 0x3f800000;
      79                 :            : }
      80                 :            : 
      81                 :            : #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
      82                 :            : 
      83                 :            : #define FPTYPE double
      84                 :            : #define FPCONST(x) x
      85                 :            : #define FPROUND_TO_ONE 0.999999999995
      86                 :            : #define FPDECEXP 256
      87                 :            : #define FPMIN_BUF_SIZE 7 // +9e+199
      88                 :            : #define fp_signbit(x) signbit(x)
      89                 :            : #define fp_isnan(x) isnan(x)
      90                 :            : #define fp_isinf(x) isinf(x)
      91                 :            : #define fp_iszero(x) (x == 0)
      92                 :            : #define fp_isless1(x) (x < 1.0)
      93                 :            : 
      94                 :            : #endif // MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT/DOUBLE
      95                 :            : 
      96                 :      42556 : static inline int fp_expval(FPTYPE x) {
      97                 :      42556 :     mp_float_union_t fb = {x};
      98                 :      42556 :     return (int)((fb.i >> MP_FLOAT_FRAC_BITS) & (~(0xFFFFFFFF << MP_FLOAT_EXP_BITS))) - MP_FLOAT_EXP_OFFSET;
      99                 :            : }
     100                 :            : 
     101                 :      45094 : int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, char sign) {
     102                 :            : 
     103                 :      45094 :     char *s = buf;
     104                 :            : 
     105         [ +  + ]:      45094 :     if (buf_size <= FPMIN_BUF_SIZE) {
     106                 :            :         // FPMIN_BUF_SIZE is the minimum size needed to store any FP number.
     107                 :            :         // If the buffer does not have enough room for this (plus null terminator)
     108                 :            :         // then don't try to format the float.
     109                 :            : 
     110         [ +  - ]:          2 :         if (buf_size >= 2) {
     111                 :          2 :             *s++ = '?';
     112                 :            :         }
     113         [ +  - ]:          2 :         if (buf_size >= 1) {
     114                 :          2 :             *s = '\0';
     115                 :            :         }
     116                 :          2 :         return buf_size >= 2;
     117                 :            :     }
     118   [ +  +  +  + ]:      45092 :     if (fp_signbit(f) && !fp_isnan(f)) {
     119                 :       8872 :         *s++ = '-';
     120                 :       8872 :         f = -f;
     121                 :            :     } else {
     122         [ +  + ]:      36220 :         if (sign) {
     123                 :         12 :             *s++ = sign;
     124                 :            :         }
     125                 :            :     }
     126                 :            : 
     127                 :            :     // buf_remaining contains bytes available for digits and exponent.
     128                 :            :     // It is buf_size minus room for the sign and null byte.
     129                 :      45092 :     int buf_remaining = buf_size - 1 - (s - buf);
     130                 :            : 
     131                 :            :     {
     132                 :      45092 :         char uc = fmt & 0x20;
     133         [ +  + ]:      45092 :         if (fp_isinf(f)) {
     134                 :       1532 :             *s++ = 'I' ^ uc;
     135                 :       1532 :             *s++ = 'N' ^ uc;
     136                 :       1532 :             *s++ = 'F' ^ uc;
     137                 :       1532 :             goto ret;
     138         [ +  + ]:      43560 :         } else if (fp_isnan(f)) {
     139                 :       1004 :             *s++ = 'N' ^ uc;
     140                 :       1004 :             *s++ = 'A' ^ uc;
     141                 :       1004 :             *s++ = 'N' ^ uc;
     142                 :       2536 :         ret:
     143                 :       2536 :             *s = '\0';
     144                 :       2536 :             return s - buf;
     145                 :            :         }
     146                 :            :     }
     147                 :            : 
     148         [ +  + ]:      42556 :     if (prec < 0) {
     149                 :        104 :         prec = 6;
     150                 :            :     }
     151                 :      42556 :     char e_char = 'E' | (fmt & 0x20);   // e_char will match case of fmt
     152                 :      42556 :     fmt |= 0x20; // Force fmt to be lowercase
     153                 :      42556 :     char org_fmt = fmt;
     154         [ +  + ]:      42556 :     if (fmt == 'g' && prec == 0) {
     155                 :          6 :         prec = 1;
     156                 :            :     }
     157                 :      42556 :     int e;
     158                 :      42556 :     int dec = 0;
     159                 :      42556 :     char e_sign = '\0';
     160                 :      42556 :     int num_digits = 0;
     161                 :      42556 :     int signed_e = 0;
     162                 :            : 
     163                 :            :     // Approximate power of 10 exponent from binary exponent.
     164                 :            :     // abs(e_guess) is lower bound on abs(power of 10 exponent).
     165                 :      42556 :     int e_guess = (int)(fp_expval(f) * FPCONST(0.3010299956639812));  // 1/log2(10).
     166         [ +  + ]:      42556 :     if (fp_iszero(f)) {
     167                 :       3122 :         e = 0;
     168         [ +  + ]:       3122 :         if (fmt == 'f') {
     169                 :            :             // Truncate precision to prevent buffer overflow
     170         [ +  + ]:       2230 :             if (prec + 2 > buf_remaining) {
     171                 :         12 :                 prec = buf_remaining - 2;
     172                 :            :             }
     173                 :       2230 :             num_digits = prec + 1;
     174                 :            :         } else {
     175                 :            :             // Truncate precision to prevent buffer overflow
     176         [ +  + ]:        892 :             if (prec + 6 > buf_remaining) {
     177                 :         40 :                 prec = buf_remaining - 6;
     178                 :            :             }
     179         [ +  + ]:        892 :             if (fmt == 'e') {
     180                 :         28 :                 e_sign = '+';
     181                 :            :             }
     182                 :            :         }
     183         [ +  + ]:      39434 :     } else if (fp_isless1(f)) {
     184                 :      16610 :         FPTYPE f_entry = f;  // Save f in case we go to 'f' format.
     185                 :            :         // Build negative exponent
     186                 :      16610 :         e = -e_guess;
     187                 :      16610 :         FPTYPE u_base = MICROPY_FLOAT_C_FUN(pow)(10, -e);
     188         [ +  + ]:      23620 :         while (u_base > f) {
     189                 :       7010 :             ++e;
     190                 :       7010 :             u_base = MICROPY_FLOAT_C_FUN(pow)(10, -e);
     191                 :            :         }
     192                 :            :         // Normalize out the inferred unit.  Use divide because
     193                 :            :         // pow(10, e) * pow(10, -e) is slightly < 1 for some e in float32
     194                 :            :         // (e.g. print("%.12f" % ((1e13) * (1e-13))))
     195                 :      16610 :         f /= u_base;
     196                 :            : 
     197                 :            :         // If the user specified 'g' format, and e is <= 4, then we'll switch
     198                 :            :         // to the fixed format ('f')
     199                 :            : 
     200   [ +  +  +  + ]:      16610 :         if (fmt == 'f' || (fmt == 'g' && e <= 4)) {
     201                 :      12776 :             fmt = 'f';
     202                 :      12776 :             dec = 0;
     203                 :            : 
     204                 :       2306 :             if (org_fmt == 'g') {
     205                 :       2306 :                 prec += (e - 1);
     206                 :            :             }
     207                 :            : 
     208                 :            :             // truncate precision to prevent buffer overflow
     209         [ +  + ]:      10470 :             if (prec + 2 > buf_remaining) {
     210                 :       1456 :                 prec = buf_remaining - 2;
     211                 :            :             }
     212                 :            : 
     213                 :      10470 :             num_digits = prec;
     214                 :      10470 :             signed_e = 0;
     215                 :      10470 :             f = f_entry;
     216                 :      10470 :             ++num_digits;
     217                 :            :         } else {
     218                 :            :             // For e & g formats, we'll be printing the exponent, so set the
     219                 :            :             // sign.
     220                 :       6140 :             e_sign = '-';
     221                 :       6140 :             dec = 0;
     222                 :            : 
     223         [ +  + ]:       6140 :             if (prec > (buf_remaining - FPMIN_BUF_SIZE)) {
     224                 :       5040 :                 prec = buf_remaining - FPMIN_BUF_SIZE;
     225         [ +  + ]:       5040 :                 if (fmt == 'g') {
     226                 :       2424 :                     prec++;
     227                 :            :                 }
     228                 :            :             }
     229                 :       6140 :             signed_e = -e;
     230                 :            :         }
     231                 :            :     } else {
     232                 :            :         // Build positive exponent.
     233                 :            :         // We don't modify f at this point to avoid inaccuracies from
     234                 :            :         // scaling it.  Instead, we find the product of powers of 10
     235                 :            :         // that is not greater than it, and use that to start the
     236                 :            :         // mantissa.
     237                 :      22824 :         e = e_guess;
     238                 :      22824 :         FPTYPE next_u = MICROPY_FLOAT_C_FUN(pow)(10, e + 1);
     239         [ +  + ]:      34534 :         while (f >= next_u) {
     240                 :      11710 :             ++e;
     241                 :      11710 :             next_u = MICROPY_FLOAT_C_FUN(pow)(10, e + 1);
     242                 :            :         }
     243                 :            : 
     244                 :            :         // If the user specified fixed format (fmt == 'f') and e makes the
     245                 :            :         // number too big to fit into the available buffer, then we'll
     246                 :            :         // switch to the 'e' format.
     247                 :            : 
     248         [ +  + ]:      22824 :         if (fmt == 'f') {
     249         [ +  + ]:      10084 :             if (e >= buf_remaining) {
     250                 :            :                 fmt = 'e';
     251         [ +  + ]:       8092 :             } else if ((e + prec + 2) > buf_remaining) {
     252                 :        964 :                 prec = buf_remaining - e - 2;
     253         [ +  + ]:        964 :                 if (prec < 0) {
     254                 :            :                     // This means no decimal point, so we can add one back
     255                 :            :                     // for the decimal.
     256                 :         28 :                     prec++;
     257                 :            :                 }
     258                 :            :             }
     259                 :            :         }
     260   [ +  +  +  + ]:      22824 :         if (fmt == 'e' && prec > (buf_remaining - FPMIN_BUF_SIZE)) {
     261                 :       4346 :             prec = buf_remaining - FPMIN_BUF_SIZE;
     262                 :            :         }
     263         [ +  + ]:      22824 :         if (fmt == 'g') {
     264                 :            :             // Truncate precision to prevent buffer overflow
     265         [ +  + ]:       8846 :             if (prec + (FPMIN_BUF_SIZE - 1) > buf_remaining) {
     266                 :       2202 :                 prec = buf_remaining - (FPMIN_BUF_SIZE - 1);
     267                 :            :             }
     268                 :            :         }
     269                 :            :         // If the user specified 'g' format, and e is < prec, then we'll switch
     270                 :            :         // to the fixed format.
     271                 :            : 
     272         [ +  + ]:      22824 :         if (fmt == 'g' && e < prec) {
     273                 :       5016 :             fmt = 'f';
     274                 :       5016 :             prec -= (e + 1);
     275                 :            :         }
     276         [ +  + ]:      22824 :         if (fmt == 'f') {
     277                 :      13108 :             dec = e;
     278                 :      13108 :             num_digits = prec + e + 1;
     279                 :            :         } else {
     280                 :            :             e_sign = '+';
     281                 :            :         }
     282                 :            :         signed_e = e;
     283                 :            :     }
     284         [ +  + ]:      42556 :     if (prec < 0) {
     285                 :            :         // This can happen when the prec is trimmed to prevent buffer overflow
     286                 :          2 :         prec = 0;
     287                 :            :     }
     288                 :            : 
     289                 :            :     // At this point e contains the absolute value of the power of 10 exponent.
     290                 :            :     // (dec + 1) == the number of dgits before the decimal.
     291                 :            : 
     292                 :            :     // For e, prec is # digits after the decimal
     293                 :            :     // For f, prec is # digits after the decimal
     294                 :            :     // For g, prec is the max number of significant digits
     295                 :            :     //
     296                 :            :     // For e & g there will be a single digit before the decimal
     297                 :            :     // for f there will be e digits before the decimal
     298                 :            : 
     299         [ +  + ]:      42556 :     if (fmt == 'e') {
     300                 :       9134 :         num_digits = prec + 1;
     301         [ +  + ]:      33422 :     } else if (fmt == 'g') {
     302         [ +  + ]:       7614 :         if (prec == 0) {
     303                 :          2 :             prec = 1;
     304                 :            :         }
     305                 :            :         num_digits = prec;
     306                 :            :     }
     307                 :            : 
     308                 :      42556 :     int d = 0;
     309         [ +  + ]:     758974 :     for (int digit_index = signed_e; num_digits >= 0; --digit_index) {
     310                 :     716418 :         FPTYPE u_base = FPCONST(1.0);
     311         [ +  + ]:     716418 :         if (digit_index > 0) {
     312                 :            :             // Generate 10^digit_index for positive digit_index.
     313                 :     227574 :             u_base = MICROPY_FLOAT_C_FUN(pow)(10, digit_index);
     314                 :            :         }
     315         [ +  + ]:    1140361 :         for (d = 0; d < 9; ++d) {
     316         [ +  + ]:    1125189 :             if (f < u_base) {
     317                 :            :                 break;
     318                 :            :             }
     319                 :     423943 :             f -= u_base;
     320                 :            :         }
     321                 :            :         // We calculate one more digit than we display, to use in rounding
     322                 :            :         // below.  So only emit the digit if it's one that we display.
     323         [ +  + ]:     716418 :         if (num_digits > 0) {
     324                 :            :             // Emit this number (the leading digit).
     325                 :     673862 :             *s++ = '0' + d;
     326         [ +  + ]:     673862 :             if (dec == 0 && prec > 0) {
     327                 :      42214 :                 *s++ = '.';
     328                 :            :             }
     329                 :            :         }
     330                 :     716418 :         --dec;
     331                 :     716418 :         --num_digits;
     332         [ +  + ]:     716418 :         if (digit_index <= 0) {
     333                 :            :             // Once we get below 1.0, we scale up f instead of calculating
     334                 :            :             // negative powers of 10 in u_base.  This provides better
     335                 :            :             // renditions of exact decimals like 1/16 etc.
     336                 :     488844 :             f *= FPCONST(10.0);
     337                 :            :         }
     338                 :            :     }
     339                 :            :     // Rounding.  If the next digit to print is >= 5, round up.
     340         [ +  + ]:      42556 :     if (d >= 5) {
     341                 :       5130 :         char *rs = s;
     342                 :       5130 :         rs--;
     343                 :      14710 :         while (1) {
     344         [ +  + ]:      14710 :             if (*rs == '.') {
     345                 :        254 :                 rs--;
     346                 :        254 :                 continue;
     347                 :            :             }
     348         [ +  + ]:      14456 :             if (*rs < '0' || *rs > '9') {
     349                 :            :                 // + or -
     350                 :          4 :                 rs++; // So we sit on the digit to the right of the sign
     351                 :          4 :                 break;
     352                 :            :             }
     353         [ +  + ]:      14452 :             if (*rs < '9') {
     354                 :       4938 :                 (*rs)++;
     355                 :       4938 :                 break;
     356                 :            :             }
     357                 :       9514 :             *rs = '0';
     358         [ +  + ]:       9514 :             if (rs == buf) {
     359                 :            :                 break;
     360                 :            :             }
     361                 :       9326 :             rs--;
     362                 :            :         }
     363         [ +  + ]:       5130 :         if (*rs == '0') {
     364                 :            :             // We need to insert a 1
     365   [ +  +  +  - ]:        192 :             if (rs[1] == '.' && fmt != 'f') {
     366                 :            :                 // We're going to round 9.99 to 10.00
     367                 :            :                 // Move the decimal point
     368                 :        128 :                 rs[0] = '.';
     369                 :        128 :                 rs[1] = '0';
     370         [ +  + ]:        128 :                 if (e_sign == '-') {
     371                 :          4 :                     e--;
     372         [ +  - ]:          4 :                     if (e == 0) {
     373                 :          4 :                         e_sign = '+';
     374                 :            :                     }
     375                 :            :                 } else {
     376                 :        124 :                     e++;
     377                 :            :                 }
     378                 :            :             } else {
     379                 :            :                 // Need at extra digit at the end to make room for the leading '1'
     380                 :            :                 // but if we're at the buffer size limit, just drop the final digit.
     381         [ +  + ]:         64 :                 if ((size_t)(s + 1 - buf) < buf_size) {
     382                 :         36 :                     s++;
     383                 :            :                 }
     384                 :            :             }
     385                 :        192 :             char *ss = s;
     386         [ +  + ]:       4772 :             while (ss > rs) {
     387                 :       4580 :                 *ss = ss[-1];
     388                 :       4580 :                 ss--;
     389                 :            :             }
     390                 :        192 :             *rs = '1';
     391                 :            :         }
     392                 :            :     }
     393                 :            : 
     394                 :            :     // verify that we did not overrun the input buffer so far
     395         [ -  + ]:      42556 :     assert((size_t)(s + 1 - buf) <= buf_size);
     396                 :            : 
     397         [ +  + ]:      42556 :     if (org_fmt == 'g' && prec > 0) {
     398                 :            :         // Remove trailing zeros and a trailing decimal point
     399         [ +  + ]:     195570 :         while (s[-1] == '0') {
     400                 :     180882 :             s--;
     401                 :            :         }
     402         [ +  + ]:      14688 :         if (s[-1] == '.') {
     403                 :       9966 :             s--;
     404                 :            :         }
     405                 :            :     }
     406                 :            :     // Append the exponent
     407         [ +  + ]:      42556 :     if (e_sign) {
     408                 :      15884 :         *s++ = e_char;
     409                 :      15884 :         *s++ = e_sign;
     410         [ +  + ]:      15884 :         if (FPMIN_BUF_SIZE == 7 && e >= 100) {
     411                 :       1132 :             *s++ = '0' + (e / 100);
     412                 :            :         }
     413                 :      15884 :         *s++ = '0' + ((e / 10) % 10);
     414                 :      15884 :         *s++ = '0' + (e % 10);
     415                 :            :     }
     416                 :      42556 :     *s = '\0';
     417                 :            : 
     418                 :            :     // verify that we did not overrun the input buffer
     419         [ -  + ]:      42556 :     assert((size_t)(s + 1 - buf) <= buf_size);
     420                 :            : 
     421                 :      42556 :     return s - buf;
     422                 :            : }
     423                 :            : 
     424                 :            : #endif // MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE

Generated by: LCOV version 1.15-5-g462f71d