我正在开发适用于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实现,但代码太长而且太依赖于其他源文件,所以我希望适应很难。
你会推荐什么样的字符串到双重和双字符串函数?
答案 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)
我知道用于打印十进制浮点数的精确值的唯一算法如下:
这是缓慢而丑陋但它有效......
答案 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"
)。