GCC和MSVC下的strtod()和sprintf()不一致

时间:2010-03-23 11:10:44

标签: string gcc double visual-c++

我正在开发适用于Windows和Mac OS X的跨平台应用,我遇到了两个标准C库函数的问题:

  • strtod() - 字符串到双重转换
  • sprintf() - 用于输出双精度浮点数时)

他们的GCC和MSVC版本以尾数的某些数字返回不同的结果。但如果指数值很大,它就扮演着重要的角色。一个例子:

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

输入测试编号在MSVC和GCC下具有相同的二进制表示。

我正在寻找一个经过良好测试的跨平台开源实现这些函数,或者仅仅是为了一对能够正确且一致地将double转换为字符串并返回的函数。

我已经尝试过clib GCC实现,但代码太长而且太依赖于其他源文件,所以我希望适应很难。

你会推荐什么样的字符串到双重和双字符串函数?

3 个答案:

答案 0 :(得分:2)

在浮点数和字符串之间转换很难 - 很难。有很多关于这个主题的论文,包括:

最后一个是关于浮点十进制算术的宝库。

GNU glibc实现可能会有所改进 - 但它不会简短或简单。


寻址示例

双倍通常存储16(有些人可能会争辩17)有效十进制数字。 MSVC正在处理17位数字。除此之外的任何事情都是噪音。 GCC正在按照您的要求进行操作,但是没有足够的位来保证您要求的额外14位数。如果您有16字节的“long double”值(SPARC,PPC,Mac的Intel x86_64),那么您可能需要保证32位有效数字。但是,你所表现出的差异是QoI;我甚至可能会说MS在这里比GCC / glibc做得更好(我不经常这么说!)。

答案 1 :(得分:0)

我知道用于打印十进制浮点数的精确值的唯一算法如下:

  1. 将尾数转换为十进制整数。您可以通过拉开位直接读取尾数来执行此操作,也可以编写一个凌乱的浮点循环,首先将该值乘以2的幂,将其置于1 <= x <10的范围内,然后拉出通过转换为int,减去并乘以10来一次关闭一个数字。
  2. 通过重复乘以或除以2来应用指数。这是对您生成的十进制数的字符串的操作。每次~3次乘法将在左侧添加一个额外的数字。每一个人都会在右边添加一个额外的数字。
  3. 这是缓慢而丑陋但它有效......

答案 2 :(得分:0)

以下函数dtoa返回一个无损转换回同一double的字符串。

如果您重写aisd来测试所有string - 到 - float实施,那么您将拥有便携式输出。

  // 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;
  }

有关MSVCRT NaN问题,请参阅Can't get a NaN from the MSVCRT strtod/sscanf/atof functions。如果您不需要识别NaN s,则可以在获得时输出无穷大("1e999")。