【问题标题】:Strangeness in Conversion from String to Double in C++在 C++ 中从 String 转换为 Double 的奇怪之处
【发布时间】:2014-05-21 09:30:21
【问题描述】:

我必须从文本文件中读取一些数据(精度为double)。有时,当使用atofstring 转换为double 时,我会得到奇怪的结果。以下是演示问题的代码 sn-p - 请注意,我使用的是 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。阅读了其他有关字符串到双精度转换的相关帖子后,我仍然无法弄清楚为什么会发生这种情况。

【问题讨论】:

  • 注意:它在我的装备上表现如预期(每个循环 9 个值)。 (Apple LLVM 版本 5.1 (clang-503.0.40)(基于 LLVM 3.4svn)),编译为 x64。有趣的是,Coliruideone.com 的结果不同。
  • 它对我也很有效,GNU C++ 4.3.2, x64
  • 在我看来 atof("-0.1") 必须作为字符串解析器工作,所以它不能像从 "-0.1" 字符串中获取 -0.1 值那样工作,它通过以下方式获得最终值将一些值行相加并将其乘以-1.0(在您的情况下为-1.0 * 0.1)。我认为这个乘法是原因。之后,您的字符串值将类似于 -0.100000000000001。
  • 它对我也有好处,VS2012,x64
  • @Fomin Arseniy 循环中没有明显的积累。

标签: c++ string double data-conversion


【解决方案1】:

二进制浮点数通常不是小数的精确表示。不同计算结果的直接比较或计算结果与文字的直接比较必然会不直观地失败。比较时必须使用间隔。这个问题与 atof 本身关系不大。

【讨论】:

    【解决方案2】:

    您的代码相当于...

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

    ...不完全等于 0。这就是实数表示的生命,它只是能够对分布在其范围内的有限数量的点进行编码的近似值,并且一些生成/处理它们的函数可能会在最后引入错误甚至对于数字来说,数字或两个数字恰好可以完美表示。在您的情况下,-0.1 无法完美表示。要了解这一点,请在this online IEEE 754 Converter 的“十进制表示”文本框中键入“-0.1”,您会看到在 32 位 floats 中可能的二进制表示只是近似...这种情况同样适用于 64 位 doubles 中可能发生的情况......你只是有一个更好的近似值。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-19
      • 2012-09-03
      • 1970-01-01
      • 2012-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多