C ++中从字符串到双精度转换的奇怪性

时间:2014-05-21 09:30:22

标签: c++ string double data-conversion

我必须从文本文件中读取一些数据(精度为double)。有时,使用stringdouble转换为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}}。阅读了关于字符串到双重转换的其他相关帖子,我仍然无法弄清楚为什么会发生这种情况。

2 个答案:

答案 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;这也是一个游戏,因为很难知道在生成一个值时累积了多少表示和舍入错误。