Strtod () and sprintf () in GCC and MSVC

I am working on a cross-platform application for Windows and Mac OS X, and I have a problem with two standard C library functions:

  • strtod() - convert strings to double
  • sprintf() - when used to output double-precision floating-point numbers)

Their versions of GCC and MSVC return different results, in some mantissa numbers. But it plays a key role if the value of the exponent is large. Example:

 MSVC: 9,999999999999999500000000000000e+032 GCC: 9,999999999999999455752309870428e+32 MSVC: 9,999999999999999500000000000000e+033 GCC: 9,999999999999999455752309870428e+33 MSVC: 9,999999999999999700000000000000e+034 GCC: 9,999999999999999686336610791798e+34 

Input test numbers have identical binary representation under MSVC and GCC.

I am looking for a well-tested cross-platform implementation of these open source functions, or just for a couple of functions that correctly and consistently convert double to string and vice versa.

I have already tried implementing the GCC click, but the code is too long and too dependent on other source files, so I expect the adaptation to be difficult.

What string-to-double and double-string implementations would you recommend?

+2
source share
3 answers

Converting between floating point numbers and strings is difficult - very difficult. There are many documents on this subject, including:

The last one is a treasury of information about decimal floating point arithmetic.

The GNU glibc implementation is likely to be about as good as it is, but it will not be concise or simple.


Addressing Examples

The double normally stores 16 (some may say 17) significant decimal digits. MSVC processes 17 digits. All but this is noise. GCC does as you ask for it, but not enough bits in double to guarantee the extra 14 digits that you request. If you have 16 byte < long double 'values ​​(SPARC, PPC, Intel x86_64 for Mac), you can require 32 significant digits. However, the differences you show are QoI; I can even claim that MS works better than GCC / glibc here (and I don't often say that!).

+1
source

The following dtoa function returns a string that, without loss, converts back to the same double .

If you rewrite aisd to test all your string float implementations, you will have portable output among them.

  // Return whether a string represents the given double. int aisd(double f, char* s) { double r; sscanf(s, "%lf", &r); return r == f; } // Return the shortest lossless string representation of an IEEE double. // Guaranteed to fit in 23 characters (including the final '\0'). char* dtoa(char* res, double f) { int i, j, lenF = 1e9; char fmt[8]; int e = floor(log10(f)) + 1; if (f > DBL_MAX) { sprintf(res, "1e999"); return res; } // converts to Inf if (f < -DBL_MAX) { sprintf(res, "-1e999"); return res; } // converts to -Inf if (isnan(f)) { sprintf(res, "NaN"); return res; } // NaNs don't work under MSVCRT // compute the shortest representation without exponent ("123000", "0.15") if (!f || e>-4 && e<21) { for (i=0; i<=20; i++) { sprintf(fmt, "%%.%dlf", i); sprintf(res, fmt, f); if (aisd(f, res)) { lenF = strlen(res); break; } } } if (!f) return res; // compute the shortest representation with exponent ("123e3", "15e-2") for (i=0; i<19; i++) { sprintf(res, "%.0lfe%d", f * pow(10,-e), e); if (aisd(f, res)) break; j = strlen(res); if (j >= lenF) break; while (res[j] != 'e') j--; res[j-1]--; if (aisd(f, res)) break; // try mantissa -1 res[j-1]+=2; if (aisd(f, res)) break; // try mantissa +1 e--; } if (lenF <= strlen(res)) sprintf(res, fmt, f); return res; } 

See Unable to get NaN from MSVCRT strtod / sscanf / atof functions for MSVCRT NaN problem. If you do not need to recognize NaN s, you can output infinity ( "1e999" ) when you receive it.

+1
source

The only algorithm I know for printing the exact value of a floating point number in decimal format is as follows:

  • Convert mantissa to decimal integer. You can do this by pulling out the bits to read the mantissa directly, or you can write a random floating point loop that first multiplies the value by two forces to put it in the range 1 <= x <10, then extrudes minus the numbers at a time discarding by int, subtracting and multiplying by 10.
  • Apply exhibitor by multiplying or dividing by 2. This is a string operation decimal digits that you generated. Every ~ 3 multiplications will add an extra digit to the left. Each individual division will add an extra digit to the right.

It is slow and ugly, but it works ...

0
source

All Articles