Python 为什么**2!=一些花车的a*a?

Python 为什么**2!=一些花车的a*a?,python,floating-point,ieee,Python,Floating Point,Ieee,我只能在Windows版的Python上复制这一点——准确地说:Python 2.7.15(v2.7.15:ca079a3ea3,2018年4月30日,16:30:26)[MSC v.1500 64位(AMD64)]在win32上。在linux2上my box安装Python(Python2.7.15(默认,2018年5月1日,20:16:04)[GCC 7.3.1 20180406])时,循环似乎没有终止,表明a**2=a*a不变量保持不变 这是怎么回事?我知道IEEE浮点数有过多的误解和特性

我只能在Windows版的Python上复制这一点——准确地说:
Python 2.7.15(v2.7.15:ca079a3ea3,2018年4月30日,16:30:26)[MSC v.1500 64位(AMD64)]在win32上
。在linux2上my box安装Python(
Python2.7.15(默认,2018年5月1日,20:16:04)[GCC 7.3.1 20180406])时,循环似乎没有终止,表明
a**2=a*a
不变量保持不变

这是怎么回事?我知道IEEE浮点数有过多的误解和特性(例如,没有回答我的问题),但我看不出规范的哪一部分或
**
的哪种实现可能允许这样做


解决重复标记:这很可能不是直接的IEEE浮点数学问题,更可能是
**
运算符的实现问题。因此,这不是一个重复的问题,这些问题只涉及精度或关联性等浮点问题。

Python的浮点运算依赖于底层平台。我假设Python的
**
操作符使用了
pow
实现(如在C中使用的)(通过引用Python确认)

通常,
pow
部分通过使用(近似)对数和指数来实现。这是必要的,因为
pow
支持非整数参数。(当然,这种通用实现并不排除其域子集的专门化。)


微软的
pow
实现是出了名的不好。因此,对于
pow(a,2)
,它可能返回的结果不等于
a*a

a**2
使用浮点幂函数(类似于标准C数学库中的幂函数),该函数能够将任意数提升为任意幂

a*a
只是相乘一次,它更适合这种情况,并且不容易出现精度错误(对于整数更是如此),就像
a**2
一样

对于浮点
a
,如果您想通过使用

$ python --version
Python 2.7.15

$ type test.py
import random

while True:
    a = random.uniform(0, 1)
    b = a ** 2
    c = a * a
    if b != c:
        print "a = {}".format(a)
        print "a ** 2 = {}".format(b)
        print "a * a = {}".format(c)
        break

$ python test.py
a = 0.145376687586
a ** 2 = 0.0211343812936
a * a = 0.0211343812936
您最好使用
a**5
,因为重复乘法现在容易出现浮点累加错误,而且速度要慢得多


a**b
b
较大时更有趣,因为它更高效。但是精度可能会有所不同,因为它使用浮点算法。

使用
repr
显示更多的数字,这样我们就可以确切地看到它失败的值以及失败的方式……或者,既然您已经在使用
格式,那么就使用
{.30}
或其他什么。这不是“浮点数学坏了”的重复。OP询问为什么
a**2
没有实现为
a*a
@EricPostpischil:IEEE 754要求正确四舍五入的运算不是幂运算。依赖于
a**2==a*a
只会带来痛苦。欢迎来到浮点,这里没有精确的东西。认真地这类事情经常发生。我们不需要在每一个应用上都提出新问题。当浮点运算中的任何东西都像数学建议的那样工作时,这应该会让我们更加惊讶。你可以在
float中看到C
pow
的用法。
float\u pow
的实现当然只适用于CPython,但是如果你研究一下Jython、IronPython或PyPy,你会发现它们在Java、.NET中使用类似的函数,还有RPython。我有一种预感,至少在linux上,小整数幂是特殊情况下的。
a*a,a**2,math.exp(2*math.log(a))
前两个似乎一直是相等的,而第三个经常是不同的。@PaulPanzer:这不一定表明整数幂是特殊情况下的。在一个具有完全正确四舍五入的幂、exp和log的平台上,您可能会看到完全相同的结果:
math.exp(2*math.log(a))
由于中间值
math.log(a)
的额外四舍五入错误,通常会出现偏差。然后,调用
exp
可能会放大该错误(取决于
a
的值)。一个好的
pow
实现应该是做一些比
exp(y*log(x))
float更复杂的事情。
不使用
math.pow
函数,至少不在CPython中。然而,它们最终都使用了C stdlib中相同的
pow
函数(经过一系列预检查和转换,这两个函数并不完全相同),这可能是您应该在这里说的。是的,稍微有点不准确。如果原始使用的数字足够多,即使是a*a也可能有精度错误。2.5*2.5可以,但2.123456789*2.123456789会有一些精度损失。@LorenPechtel
2.5*2.5
只起作用,因为它可以用只有几位的二进制表示。请尝试
2.2*2.2
4.840000000000001
)。@jpmc26是。我特意选择了一个用浮点表示的短值,这样就不会有精度损失。
a * a * a * a * a