其他分享
首页 > 其他分享> > C中从字符串到双精度转换的奇怪性

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

作者:互联网

我必须从文本文件中读取一些数据(双精度).有时,当使用atof从string转换为double时,我会得到奇怪的结果.以下是演示该问题的代码片段 – 请注意我使用的是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 | 

请注意,基于字符串赋值(a_string)的for循环比基于内联赋值(a_inline)的循环运行一次 – 请注意最后一个数字-3.46945e-18.阅读了关于字符串到双重转换的其他相关帖子,我仍然无法弄清楚为什么会发生这种情况.

解决方法:

你的代码相当于……

std::atof("-0.1") - 10 * 1e-2

…不完全等于0.这是实数表示的生命,它只是能够编码在其范围内扩展的有限数量的点的近似值,并且生成/处理它们的一些函数可能在最后一个数字或两个数字中引入错误即使是数字也不是完全可以代表的.在您的情况下,-0.1无法完美呈现.要了解这一点,在this online IEEE 754 Converter的“十进制表示”文本框中键入“-0.1”,您将看到32位浮点数中可能的二进制表示只是近似…在这种情况下同样如此在64位双打中有什么可能……你只是有一个更好的近似值.

通常的建议是阅读What Every Computer Scientist Should Know About Floating Point Values

什么是数学上“x – x”可能不是0当每个x的计算以不同的方式或在不同的时间完成时,它的确切方式可以根据优化级别,周围的代码,硬件,编译器等而变化.它有点设计代码的艺术形式,优雅地处理这些问题.在你的情况下,如果你想要循环的9次迭代然后计数到9,如果你想在比较中有一些容差,那么在比较中写一些允许小误差(“epsilon”值)的东西.您还可以使用一次一位地将浮点数缓冲到下一个可表示值的函数,这避免了缩放epsilon值的问题,因此它对应于涉及特定指数的最后一个或两个有效数字,但这有点像一个游戏,因为很难知道在生成一个值时累积了多少表示和舍入错误.

标签:data-conversion,c,string,double
来源: https://codeday.me/bug/20190825/1715345.html