8

我想知道这是否属实:当我取平方整数的平方根时,例如

f = Math.sqrt(123*123)

我会得到一个非常接近的浮点数123。由于浮点表示精度,这可能类似于 122.99999999999999999999 或 123.000000000000000000001。

因为floor(122.999999999999999999)是 122,我应该得到 122 而不是 123。所以我预计floor(sqrt(i*i)) == i-1在大约 50% 的情况下会这样。奇怪的是,对于我测试过的所有数字,floor(sqrt(i*i) == i. 这是一个测试前 1 亿个数字的小 ruby​​ 脚本:

100_000_000.times do |i|
  puts i if Math.sqrt(i*i).floor != i
end

上面的脚本从不打印任何东西。为什么呢?

更新:感谢您的快速回复,这似乎是解决方案:根据维基百科

任何绝对值小于或等于 2^24 的整数都可以用单精度格式精确表示,任何绝对值小于或等于 2^53 的整数都可以用双精度格式精确表示。

Math.sqrt(i*i) 从 i=9007199254740993,即 2^53 + 1 开始,开始按照我的预期运行。

4

4 回答 4

23

这是您困惑的本质:

由于浮点表示精度,这可能类似于 122.99999999999999999999 或 123.000000000000000000001。

这是错误的。在符合 IEEE-754 的系统上,它总是正好是 123,这几乎是现代所有系统。浮点运算没有“随机误差”或“噪声”。它具有精确的、确定性的舍入,并且许多简单的计算(比如这个)根本不会产生任何舍入。

123完全可以用浮点表示,所以也是123*123所有中等大小的整数也是如此)。123*123因此,当您转换为浮点类型时,不会发生舍入错误。结果正好 15129

根据 IEEE-754 标准,平方根是正确的舍入运算。这意味着如果有一个确切的答案,则需要平方根函数来产生它。由于您正在取 的平方根,也就是正是您从平方根函数得到的结果不会发生舍入或近似。15129 123

现在,对于多大的整数,这将是正确的?

双精度可以精确表示最大为 2^53 的所有整数。因此,只要i*i小于 2^53,您的计算中就不会发生舍入,因此结果将是准确的。这意味着对于所有i小于94906265,我们知道计算将是精确的。

但你尝试i的比这更大!发生了什么?

对于i您尝试过的最大的,i*i仅略大于 2^53 (1.1102... * 2^53实际上)。因为从整数到双精度的转换(或双精度乘法)也是正确的舍入运算,i*i所以将是最接近 的精确平方的可表示值i。在这种情况下,由于i*i54 位宽,舍入将发生在最低位。因此我们知道:

i*i as a double = the exact value of i*i + rounding

rounding要么在哪里-1,0, or 1。如果四舍五入为零,则平方是精确的,所以平方根是精确的,所以我们已经知道你得到了正确的答案。让我们忽略这种情况。

所以现在我们正在研究 的平方根i*i +/- 1。使用泰勒级数展开,这个平方根的无限精确(未四舍五入)值为:

i * (1 +/- 1/(2i^2) + O(1/i^4))

现在看看你之前是否没有做过任何浮点错误分析有点繁琐,但是如果你使用这个事实i^2 > 2^53,你可以看到:

1/(2i^2) + O(1/i^4)

term 小于 2^-54,这意味着(由于平方根被正确舍入,因此它的舍入误差必须小于 2^54),sqrt 函数的舍入结果正好是i.

事实证明(通过类似的分析),对于任何可精确表示的浮点数 x,sqrt(x*x) 恰好是 x(假设 的中间计算x*x没有上溢或下溢),所以你唯一的方法这种类型的计算可能会遇到舍入是在其x自身的表示中,这就是为什么您会看到它从2^53 + 1(最小的不可表示的整数)开始。

于 2010-01-13T22:18:42.397 回答
15

对于“小”整数,通常有一个精确的浮点表示。

于 2010-01-13T21:42:17.297 回答
7

不难发现这种情况会如您所料那样崩溃:

Math.sqrt(94949493295293425**2).floor
# => 94949493295293424
Math.sqrt(94949493295293426**2).floor
# => 94949493295293424
Math.sqrt(94949493295293427**2).floor
# => 94949493295293424
于 2010-01-13T21:44:38.707 回答
3

Ruby 的 Float 是一个双精度浮点数,这意味着它可以准确地表示具有(经验法则)大约 16 个有效十进制数字的数字。对于常规的单精度浮点数,它大约是 7 位有效数字。

您可以在这里找到更多信息:

每个计算机科学家都应该知道的关于浮点运算的知识:http: //docs.sun.com/source/819-3693/ncg_goldberg.html

于 2010-01-13T22:09:13.903 回答