Precise floating-point<->string conversion

后端 未结 5 481
误落风尘
误落风尘 2020-12-09 05:03

I am looking for a library function to convert floating point numbers to strings, and back again, in C++. The properties I want are that str2num(num2str(x)) == x and that nu

5条回答
  •  囚心锁ツ
    2020-12-09 05:34

    I think this does what you want, in combination with the standard library's strtod():

    #include 
    #include 
    
    int dtostr(char* buf, size_t size, double n)
    {
      int prec = 15;
      while(1)
      {
        int ret = snprintf(buf, size, "%.*g", prec, n);
        if(prec++ == 18 || n == strtod(buf, 0)) return ret;
      }
    }
    

    A simple demo, which doesn't bother to check input words for trailing garbage:

    int main(int argc, char** argv)
    {
      int i;
      for(i = 1; i < argc; i++)
      {
        char buf[32];
        dtostr(buf, sizeof(buf), strtod(argv[i], 0));
        printf("%s\n", buf);
      }
      return 0;
    }
    

    Some example inputs:

    % ./a.out 0.1 1234567890.1234567890 17 1e99 1.34 0.000001 0 -0 +INF NaN
    0.1
    1234567890.1234567
    17
    1e+99
    1.34
    1e-06
    0
    -0
    inf
    nan
    

    I imagine your C library needs to conform to some sufficiently recent version of the standard in order to guarantee correct rounding.

    I'm not sure I chose the ideal bounds on prec, but I imagine they must be close. Maybe they could be tighter? Similarly I think 32 characters for buf are always sufficient but never necessary. Obviously this all assumes 64-bit IEEE doubles. Might be worth checking that assumption with some kind of clever preprocessor directive -- sizeof(double) == 8 would be a good start.

    The exponent is a bit messy, but it wouldn't be difficult to fix after breaking out of the loop but before returning, perhaps using memmove() or suchlike to shift things leftwards. I'm pretty sure there's guaranteed to be at most one + and at most one leading 0, and I don't think they can even both occur at the same time for prec >= 10 or so.

    Likewise if you'd rather ignore signed zero, as Javascript does, you can easily handle it up front, e.g.:

    if(n == 0) return snprintf(buf, size, "0");
    

    I'd be curious to see a detailed comparison with that 3000-line monstrosity you dug up in the Python codebase. Presumably the short version is slower, or less correct, or something? It would be disappointing if it were neither....

提交回复
热议问题