【问题标题】:Can float values add to a sum of zero? [duplicate]浮点值可以加到零和吗? [复制]
【发布时间】:2011-12-17 10:27:29
【问题描述】:

可能重复:
Most effective way for float and double comparison

我有两个值(浮点数),我试图将它们加在一起并取平均值。我遇到的问题是,这些值有时会加起来为零,因此不需要对它们进行平均。

我所处的情况具体包含值“-1”和“1”,但当加在一起时,我得到的值是“-1.19209e-007”,显然不是 0。有这方面的信息吗?

【问题讨论】:

  • 你永远不能测试浮点值是否相等(就像你在这里所做的那样,用0检查是否相等)。谷歌搜索(甚至在这里搜索)会产生很多结果。
  • Uhmmm (a - a) / 2,如果 a 为 1,则为 0。总是。如果我们考虑舍入误差,它也不能产生除零以外的浮点数。你确定这是问题所在?
  • @SalvatorePreviti:不正确。 (a-a) 并不总是给浮点数 0,因为当寄存器中的浮点数存储到内存中时会发生舍入。
  • @MooingDuck 但肯定(1.+ -1.) 将始终完全为零,因为1.-1. 都可以精确表示(通常)。
  • @Rob 这些值表示正在摄取的文件中的 x 坐标值。在这种情况下,它们正好是 -1 和 1,但是存储在浮点数据类型中,因为它们并不总是这些值。

标签: c++ c floating-point


【解决方案1】:

至少马上,这听起来像是典型的浮点不精确。

处理它的常用方法是将数字四舍五入到正确的有效数字位数。在这种情况下,您的平均值为-1.19209e-08(即0.00000001192)。到(比如说)六或七位有效数字,那 零。

【讨论】:

  • 这听起来不像典型的浮点不精确。在这种情况下,-11 都可以精确表示,添加它们应该得到0
  • @JamesKanze:如果它们真的是 1 和 -1,那将是真的。我的猜测(我可能应该说)是 1 和/或 -1 是以前的一些计算的结果,从数学上看应该得到这个数字,但由于四舍五入只接近它。跨度>
  • 这些值是特定网格中的最小和最大 x 坐标值。这些值是从文件中提取的并存储在浮点数组中。接下来,我使用 min/max 函数对所有 x 值进行比较,以确定最大值和最小值。在尝试对它们进行平均后,我遇到了这个问题。
  • Evan:当您从流中读取浮点数时,即使它们可以精确表示,值也可能不准确。
  • @MooingDuck 如果可以准确表示该值,并且从流中读取时的结果不准确,那么这是库中的错误。准确读取的算法是众所周知的。
【解决方案2】:

将所有数字的总和除以您的计数。在您进行打印、报告比较或您正在做的任何事情之前,将您的答案四舍五入。

【讨论】:

    【解决方案3】:

    再次,对此进行一些搜索,但这是基本解释...

    计算机以 2 为底而不是以 10 为底来近似浮点数。这意味着,例如,0.2(当转换为二进制时)实际上是 0.001100110011 ...永远。由于计算机不能永远添加这些,它必须近似它。

    由于这些近似值,我们失去了计算的“精度”。因此“单”和“双”精度浮点数。这就是为什么您从不测试浮点数实际上为 0 的原因。相反,您测试是否低于您想要用作零的某个阈值。

    【讨论】:

    • 抱歉,测试0.0 的浮点值是有效且频繁的操作。
    • 可能是,但确实不应该这样做。经过很多(如果有的话)处理浮点数而不是简单的加法或减法(即使那样可能会导致问题),那么“零”几乎可以显示为任何任意小的数字。
    • 如果您知道自己在做什么,则不会。在某些情况下,与0.0 进行比较(或更一般地,使用==!= 进行比较)是有效的,而在某些情况下则不是。没有绝对的规则;您必须了解机器浮点的实际工作原理,并据此进行判断。
    【解决方案4】:

    我很抱歉,但这对我来说没有意义。 两个浮点值,如果它们完全相同但符号相反,相减将始终产生 0。这就是浮点运算的工作原理。

    float a = 0.2f;
    float b = -0.2f;
    float f = (a - b) / 2;
    printf("%f %d\n", f, f != 0); // will print out 0.0000 0
    

    如果编译器不优化代码,也将始终为 0。 如果 a 和 b 具有相同的值但符号相反,则无需考虑任何类型的舍入误差!即如果a的高位为0,b的高位为1,其他位相同,则结果不能为0。

    但如果 a 和 b 稍有不同,当然,结果可以是非零的。

    避免这种情况的一种可能解决方案是使用公差...

    float f = (a + b) / 2;
    
    if (abs(f) < 0.000001f)
        f = 0;
    

    我们正在使用一个简单的容差来查看我们的值是否接近于零。

    一个很好的示例代码来说明这一点......

    int main(int argc)
    {
        for (int i = -10000000; i <= 10000000 * argc; ++i)
        {
            if (i != 0)
            {
                float a = 3.14159265f / i;
                float b = -a + (argc - 1);
    
                float f = (a + b) / 2;
    
                if (f != 0)
                    printf("%f %d\n", a, f);
            }
        }
        printf("completed\n");
        return 0;
    }
    

    我在这里使用“argc”作为强制编译器不优化我们的代码的技巧。

    【讨论】:

    • 它们一定是不同的,但我无法区分它们。
    • 这也让我感到困惑,因为 +1 和 -1 可以精确地以浮点格式表示,总和应该正好为 0,除非 1 和 -1 本身不精确(例如如果它们是一些小数的总和)。哦,好吧,问题现在已经解决了,SO 霸主无论如何都不会容忍讨论这些数字是如何产生的,因为它不会以回答问题的形式 =)
    • +1 + -1 = 0 :) 使用 int、float、double。所以我不知道他为什么这么说:)
    • @SamSkuce:这取决于它们是如何设置的。他说它们是从文件中读取的,这意味着它们必须由字符组合而成,这意味着涉及数学,这意味着存在舍入错误的空间。
    猜你喜欢
    • 2013-02-19
    • 2011-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多