简体   繁体   English

Python乘法失去浮点精度

[英]Python multiplication losing floating point precision

I was working with floating point numbers recently and I realized a something I didn't expect about floating point numbers.我最近在处理浮点数,我意识到浮点数出乎我的意料。 Here is an example这是一个例子

a = 0.1
print(f"{a:0.20f}")
#'0.10000000000000000555'
b = a * 10
print(f"{b:0.20f}")
#'1.00000000000000000000'

I would expect the last print to output 1.00000000000000005551 (ie, 1 followed by digits 1 through 21 of 0.1 ).我希望最后一次打印输出1.00000000000000005551 (即 1 后跟数字 1 到 21 of 0.1 )。

What I am curious about is why the floating point error disappears when multiplying by 10. The normal rules of arithmetic suggests that the floating point error would be propagated, but that isn't actually happening.我很好奇的是为什么当乘以 10 时浮点误差会消失。算术的正常规则表明浮点误差会被传播,但这实际上并没有发生。 Why does this take place?为什么会发生这种情况? Is there a way to avoid it?有没有办法避免它?

The exact real number arithmetic product of 10 and 0.1000000000000000055511151231257827021181583404541015625, the IEEE 754 64-bit binary representation of 0.1, is 1.000000000000000055511151231257827021181583404541015625.的10和0.1000000000000000055511151231257827021181583404541015625确切实数算术乘积,0.1 IEEE 754的64位二进制表示,是1.000000000000000055511151231257827021181583404541015625。

It is not exactly representable.它不能完全代表。 It is bracketed by 1.0 and 1.0000000000000002220446049250313080847263336181640625它由 1.0 和 1.00000000000000022204460492503130808472633336181640625 括起来

It is closer to 1.0, so that is the round-to-nearest result of the multiplication.它更接近 1.0,因此这是乘法的舍入到最接近的结果。

I calculated the numbers using a short Java program:我使用一个简短的 Java 程序计算了这些数字:

import java.math.BigDecimal;

public strictfp class Test {
    public static void main(String[] args) {
        BigDecimal rawTenth = new BigDecimal(0.1);
        BigDecimal realProduct = rawTenth.multiply(BigDecimal.TEN);
        System.out.println(realProduct);
        System.out.println(new BigDecimal(Math.nextUp(1.0)));
    }
}

Output:输出:

1.0000000000000000555111512312578270211815834045410156250
1.0000000000000002220446049250313080847263336181640625

This answer shows how you can determine that converting 1/10 to floating-point and multiplying by 10 will produce exactly 1 using just a little arithmetic;这个答案显示了如何确定将 1/10 转换为浮点数并乘以 10 只需使用一点算术即可得出正好为 1; there is no need to calculate large or precise numbers.无需计算大量或精确的数字。

Your Python implementation uses the common IEEE-754 binary64 format.您的 Python 实现使用常见的 IEEE-754 binary64 格式。 (Python is not strict about which floating-point format implementations should use.) In this format, numbers are represented, in effect, as a sign (+ or −) applied to some 53-bit integer multiplied by some power of two. (Python 对应该使用哪种浮点格式的实现并不严格。)在这种格式中,数字实际上表示为应用于某个 53 位整数乘以某个 2 的幂的符号(+ 或 -)。 Because 2 −4 ≤ 1/10 < 2 −3 , the representable number nearest 1/10 is some integer M multiplied by 2 −3−53 .因为 2 -4 ≤ 1/10 < 2 -3 ,最接近 1/10 的可表示数是某个整数 M 乘以 2 -3-53 (The −53 scales the 53-bit integer to between ½ and 1, and the −3 scales that to between 2 −4 and 2 −3 .) Let's call that representable number x. (-53 将 53 位整数缩放到 ½ 和 1 之间,而 -3 将其缩放到 2 -4和 2 -3 之间。)让我们称其为可表示数 x。

Then we have x = M•2 −56 = 1/10 + e, where e is some rounding error that occurs when we round 1/10 to the nearest representable value.然后我们有 x = M•2 −56 = 1/10 + e,其中 e 是当我们将 1/10 舍入到最接近的可表示值时发生的一些舍入误差。 Since we round to the nearest representable value, |e|由于我们四舍五入到最接近的可表示值,|e| ≤ ½•2 −56 = 2 −57 . ≤ ½•2 −56 = 2 −57

To find exactly what e is, multiply 1/10 by 2 56 .要准确找出 e 是什么,请将 1/10 乘以 2 56 WolframAlpha tells us it is 7205759403792793+3/5. WolframAlpha告诉我们它是 7205759403792793+3/5。 To get the nearest representable value, we should round up, so M = 7205759403792794 and e = 2/5 • 2 −56 .为了获得最接近的可表示值,我们应该向上取整,因此 M = 7205759403792794 和 e = 2/5 • 2 −56 Although I used WolframAlpha to illustrate this, we do not need M, and we can find e by observing the pattern in powers of two modulo 10: 2 1 →2, 2 2 →4, 2 3 →8, 2 4 →6, 2 5 →2, 2 6 →4, and so the pattern repeats with a cycle of 4, and 56 modulo 4 is 0, so 2 56 modulo 10 has the same remainder as 2 4 , 6, so the fraction is 6/10 = 3/5.虽然我用 WolframAlpha 来说明这一点,但我们不需要 M,我们可以通过观察两个模 10 的幂的模式找到 e:2 1 →2, 2 2 →4, 2 3 →8, 2 4 →6, 2 5 →2, 2 6 →4,所以模式以4为循环重复,56模4为0,所以2 56模10与2 4 , 6有相同的余数,所以分数是6/10 = 3/5。 We know that should round to the nearest integer, 1, so e = 2/5 • 2 −56 .我们知道应该四舍五入到最接近的整数 1,所以 e = 2/5 • 2 −56

So x = M•2 −56 = 1/10 + 2/5•2 −56 .所以 x = M•2 −56 = 1/10 + 2/5•2 −56

Now we can figure out the result of computing 10•x with floating-point arithmetic.现在我们可以计算出用浮点算法计算 10•x 的结果。 The result is as if we first compute 10•x with real-number arithmetic and then round to the nearest representable value.结果就像我们首先用实数算术计算 10•x 然后四舍五入到最接近的可表示值。 In real-number arithmetic, 10•x = 10•(1/10 + 2/5•2 −56 ) = 1 + 10•2/5•2 −56 = 1 + 4•2 −56 = 1 + 2 −54 .在实数算术中,10•x = 10•(1/10 + 2/5•2 −56 ) = 1 + 10•2/5•2 −56 = 1 + 4•2 −56 = 1 + 2 − 54 . The two neighboring representable values are 1 and 1 + 2 −52 , and 1 + 2 −54 is closer to 1 than it is to 1 + 2 −52 .两个相邻的可表示值是 1 和 1 + 2 -52 ,并且 1 + 2 -54比 1 + 2 -52更接近于 1。 So the result is 1.所以结果是1。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM