我必须从文本文件中读取一些数据(精度为double
)。有时,使用string
从double
转换为atof
时,我会得到奇怪的结果。以下是演示该问题的代码片段 - 请注意我使用的是GNU C ++ 4.8.1编译器。代码只是打印出介于-0.1和0之间的数字,步长为0.01;所以只有9个数字。
#include <iostream>
#include <cstdlib>
int main() {
int y;
double a, b = 0.0, x = 1e-2, z;
double a_string = atof("-0.1");
double a_inline = -0.1;
std::cout << "Inline:: ";
a = a_inline;
for (y = 1 ; (z = a + y * x) < b; ++y){
std::cout << y << ": " << z << " | ";
}
std::cout << "\nString:: ";
a = a_string;
for (y = 1 ; (z = a + y * x) < b; ++y){
std::cout << y << ": " << z << " | ";
}
return 0;
}
结果是
Inline:: 1: -0.09 | 2: -0.08 | 3: -0.07 | 4: -0.06 | 5: -0.05 | 6: -0.04 | 7: -0.03 | 8: -0.02 | 9: -0.01 |
String:: 1: -0.09 | 2: -0.08 | 3: -0.07 | 4: -0.06 | 5: -0.05 | 6: -0.04 | 7: -0.03 | 8: -0.02 | 9: -0.01 | 10: -3.46945e-18 |
请注意,基于字符串赋值(for
)的a_string
循环运行时间比基于内联赋值(a_inline
)的循环多一次 - 请注意最后一个数字{ {1}}。阅读了关于字符串到双重转换的其他相关帖子,我仍然无法弄清楚为什么会发生这种情况。
答案 0 :(得分:4)
二进制浮点数通常不是小数部分的精确表示。不同计算结果或计算结果与文字的直接比较必然会不直观地失败。比较时必须使用间隔。这个问题与此无关。
答案 1 :(得分:2)
您的代码相当于......
std::atof("-0.1") - 10 * 1e-2
...不完全等于0.这就是实数表示的生命,它只是近似能够编码遍布其范围的有限数量的点,并且一些生成/处理它们的函数可能会引入即使对于数字而言,最后一个数字或两个数字中的错误恰好可以完美表示。在您的情况下,-0.1无法完美呈现。要了解这一点,请键入&#34; -0.1&#34;进入&#34;十进制表示&#34; this online IEEE 754 Converter中的文本框,您会发现32位float
中的二进制表示只是近似的......在这种情况下,&#39; s同样适用于&# 39;可能在64位double
s ...你只是有一个更好的近似值。
通常的建议是阅读What Every Computer Scientist Should Know About Floating Point Values
在数学上是什么&#34; x - x&#34;当每个x的计算以不同的方式或在不同的时间完成时,可能不是0,并且它的精确度如何随着优化级别,周围的代码,硬件,编译器等而变化。它是一种艺术形式设计能够优雅地处理这些问题的代码。在你的情况下,如果你想要循环的9次迭代然后计数到9,如果你想在比较中有一些容差,那么在比较中写一些允许误差很小的东西(&#34; epsilon&#34;值) 。您还可以使用一次一位地将浮点数碰撞到下一个可表示值的函数,这避免了缩放epsilon值的问题,因此它对应于涉及特定指数的最后一个或两个有效数字,但是&#39;这也是一个游戏,因为很难知道在生成一个值时累积了多少表示和舍入错误。