【问题标题】:Floating point representations seem to do integer arithmetic correctly - why?浮点表示似乎正确地进行整数运算 - 为什么?
【发布时间】:2012-11-16 09:37:02
【问题描述】:

我一直在玩浮点数,根据我过去对它们的了解,0.1 + 0.2 最终变成0.30000000000000004 这样的事实并不让我感到惊讶.

确实让我惊讶的是,整数运算总是似乎工作得很好,并且没有任何这些伪像。

我首先在 JavaScript(node.js 中的 Chrome V8)中注意到了这一点:

0.1 + 0.2 == 0.3 // false, NOT surprising
123456789012 + 18 == 123456789030  // true
22334455667788 + 998877665544 == 23333333333332 // true
1048576 / 1024 == 1024  // true

C++(Mac OS X 上的 gcc)似乎具有相同的属性。

最终结果似乎是整数——因为没有更好的词——工作。只有当我开始使用十进制数字时,事情才会变得不稳定。

这是设计的特征、数学工件还是编译器和运行时环境进行的某些优化?

【问题讨论】:

  • 尝试使用大于2^53 的数字,您会看到。 :)
  • 整数一直有效,直到你使它们足够大。在典型的浮点实现的尾数中有 53 位(我认为)。这对于一些大整数来说已经足够了,但是让它们足够大,你就会遇到问题。
  • 22334455667788 在 JavaScript 中是整数还是浮点常量?
  • 这个问题有很多很好的答案,都围绕着浮点尾数可以准确表示小于 2^53 的所有整数这一事实。
  • 所以,对于我没有选择的所有答案,我深表歉意。只能选择一个@_@

标签: javascript c++ floating-point


【解决方案1】:

这是设计的特征、数学工件还是编译器和运行时环境进行的某些优化?

这是实数的一个特征。现代代数的一个定理(现代代数,不是高中代数;数学专业的学生在他们的基本微积分和线性代数课程之后上现代代数课程)说,对于某个正整数 b,任何正实数r 可以表示为 r = a * bp,其中 a 是在 [1,b) 中,p 是某个整数。例如,102410 = 1.02410*103。正是这个定理证明了我们使用科学记数法的合理性。

那个数字 a 可以分为终端(例如 1.0)、重复(1/3=0.333...)或非重复(pi 的表示)。这里有一个终端号码的小问题。任何终端编号也可以表示为重复编号。例如,0.999... 和 1 是同一个数字。这种表示上的歧义可以通过指定可以表示为终端号的数字来解决。

您发现的结果是所有整数在任何基数中都有一个终端表示。

这里有一个关于实数在计算机中的表示方式的问题。正如intlong long int 不代表所有整数一样,floatdouble 也不代表所有实数。大多数计算机上用来表示实数 r 的方案是以 r = a*2p 的形式表示sup>,但尾数(或有效数)a 被截断为一定数量的位,而指数 p 被限制为某个有限数。这意味着某些整数无法精确表示。例如,即使 googol (10100) 是整数,它的浮点表示也不准确。 googol 的 base 2 表示是一个 333 位的数字。这个 333 位的尾数被截断为 52+1 位。

这样做的结果是双精度算术不再精确,即使对于整数来说,如果所讨论的整数大于 253。在 253 和 264 之间的值上使用 unsigned long long int 类型尝试您的实验。您会发现对于这些大整数,双精度算术不再精确。

【讨论】:

    【解决方案2】:

    我在假设 Javascript 对所有数字使用双精度浮点表示的情况下写这篇文章。

    某些数字具有浮点格式的精确表示,特别是所有整数,例如|x| < 2^53。一些数字不会,特别是像 0.1 或 0.2 这样的小数,它们在二进制表示中变成无限小数。

    如果所有操作数和运算结果都有精确的表示,那么使用==比较结果是安全的。

    相关问题:

    What number in binary can only be represented as an approximation?

    Why can't decimal numbers be represented exactly in binary?

    【讨论】:

    • 您可以将其扩展为 |x|
    【解决方案3】:

    在可表示范围内的整数完全可以被机器表示,浮点数不是(嗯,大多数)。

    如果您通过“基本整数数学”理解“特征”,那么是的,您可以假设正确实现算术是一个特征。

    【讨论】:

    • OP 显然假设他的示例中的所有数字都是浮点类型
    • @Andrey 他似乎完全意识到它们是整数 - “整数运算似乎总是可以正常工作”
    • 通过阅读标题,我认为他的意思可能是“浮点运算似乎总是可以很好地处理整数”。我不知道javascript中这些常量的数据类型是什么
    • @Andrey:Javascript 中只有一种数字类型,大多数其他语言将其描述为double。因此,“整数算术”和“整数浮点算术”之间的 Javascript 没有区别。当然,这是“逻辑上的”——我不知道 Javascript 优化器是否检测到它们何时可以使用整数操作
    • @SteveJessop 2+2 被执行为(float)2+(float)2,对吧?
    【解决方案4】:

    原因是,您可以用二进制格式(0001、0010、0011、...)精确地表示每个整数(1、2、3、...)

    这就是整数总是正确的原因,因为 0011 - 0001 总是 0010。 浮点数的问题是,点后面的部分不能完全转换成二进制。

    【讨论】:

      【解决方案5】:

      您所说的“工作”的所有情况都是您给出的数字可以准确以浮点格式表示。您会发现添加 0.25 和 0.5 和 0.125 也完全可以,因为它们也可以精确地表示为二进制浮点数。

      只有不能为 0.1 之类的值才会得到看似不准确的结果。

      【讨论】:

        【解决方案6】:

        整数是精确的,因为不精确主要是由于我们编写小数的方式造成的,其次是因为许多有理数在任何给定的基数中根本没有不重复的表示。

        请参阅:https://stackoverflow.com/a/9650037/140740 了解完整说明。

        【讨论】:

          【解决方案7】:

          该方法仅在您将足够小的整数添加到非常大的整数时才有效 - 即使在这种情况下,您也不会以“浮点”格式表示两个整数。

          【讨论】:

            【解决方案8】:

            无法表示所有浮点数。这是由于它们的编码方式。维基页面比我解释得更好:http://en.wikipedia.org/wiki/IEEE_754-1985。 因此,当您尝试比较浮点数时,应该使用 delta:

            myFloat - expectedFloat < delta
            

            您可以使用最小的可表示浮点数作为增量。

            【讨论】:

            • 这不是办法。比较应该与测试的值成比例地进行。这意味着将浮点数或双精度数减去 整数 并测试 delta
            • “几乎相等”是一种高级技术。除非您完全理解其含义,否则不要使用它。特别是,当a 几乎等于b 并且b 几乎等于c 时,并不意味着a 几乎等于c
            猜你喜欢
            • 2011-08-01
            • 2017-09-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-06-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多