我每天都在使用Python 2.4在我的公司工作。我使用了标准数学库中的多功能对数函数'log',当我输入log(2 ** 31,2)时,它返回31.000000000000004,这让我感到有些奇怪。
我用2的其他力量做了同样的事情,它完美地运作了。我跑了'log10(2 ** 31)/ log10(2)',我得到了一轮31.0
我尝试在Python 3.0.1中运行相同的原始函数,假设它已在更高级的版本中修复。
为什么会这样? Python中的数学函数可能存在一些不准确之处吗?
答案 0 :(得分:47)
这可以通过计算机算术来实现。遵循特定的规则,例如IEEE 754,可能与您在学校学到的数学不符。
如果实际上很重要,请使用Python的decimal type。
示例:
from decimal import Decimal, Context
ctx = Context(prec=20)
two = Decimal(2)
ctx.divide(ctx.power(two, Decimal(31)).ln(ctx), two.ln(ctx))
答案 1 :(得分:20)
你应该阅读“每个计算机科学家应该知道的关于浮点算术的内容”。
答案 2 :(得分:17)
始终假设浮点运算会在其中出现一些错误并检查是否存在将该错误考虑在内的相等性(百分比值如0.00001%或固定值如0.00000000001)。这种不准确性是给定的,因为不是所有的十进制数都可以用二进制表示,具有固定的位精度。
如果Python使用IEEE754,那么你的特殊情况不是其中之一,因为31应该可以很容易地用单精度表示。然而,它可能会在计算log 2 2 31 所需的许多步骤之一中失去精度,原因很简单,因为它没有代码来检测特殊情况,例如a两个直接的力量。
答案 3 :(得分:5)
浮点运算永远不会完全正确。对于语言/硬件基础结构,它们返回具有可接受的相对误差的结果。
一般来说,假设浮点运算是精确的,特别是单精度运算是错误的。来自维基百科的"Accuracy problems" section浮点文章:)
答案 4 :(得分:3)
IEEE双浮点数具有52 bits of precision。从10 ^ 15< 2 ^ 52< 10 ^ 16,双倍有15到16位有效数字。结果31.000000000000004对16位数是正确的,因此它可以达到预期的效果。
答案 5 :(得分:2)
这很正常。我希望log10比log(x,y)更精确,因为它确切地知道对数的基数是什么,也可能有一些硬件支持来计算基数为10的对数。
答案 6 :(得分:2)
浮动不精确
我不赞成这种说法,因为在大多数平台上(带有底层IEEE 754浮点数),确切地表示了2的幂。
因此,如果我们真的希望精确的2的log2是精确的,我们可以。
我将在Squeak Smalltalk中进行演示,因为在该语言中更改基础系统很容易,但是该语言并不重要,浮点计算是通用的,Python对象模型与Smalltalk相距不远。
要获取以n为底的对数,可以使用Number中定义的log:函数,该函数天真的使用尼泊尔对数ln
:
log: aNumber
"Answer the log base aNumber of the receiver."
^self ln / aNumber ln
self ln
(采用接收器的尼泊尔对数),aNumber ln
和/
这三个操作将把结果四舍五入到最接近的Float,这些四舍五入的误差会累积起来……所以天真的实现会受到您所观察到的舍入误差的影响,我猜想log函数的Python实现没有太大区别。
((2 raisedTo: 31) log: 2) = 31.000000000000004
但是如果我这样更改定义:
log: aNumber
"Answer the log base aNumber of the receiver."
aNumber = 2 ifTrue: [^self log2].
^self ln / aNumber ln
在Number类中提供通用的log2:
log2
"Answer the base-2 log of the receiver."
^self asFloat log2
和Float类的改进:
log2
"Answer the base 2 logarithm of the receiver.
Care to answer exact result for exact power of two."
^self significand ln / Ln2 + self exponent asFloat
其中Ln2
是一个常数(2 ln),然后我有效地得到了一个精确的log2,精确为2的幂,因为该数的有效位数= 1.0(包括Squeak指数/有效位数定义的次正规数),以及{ {1}}。
该实现非常简单,并且可以在Python中转换(可能在VM中)没有困难;运行时的成本非常便宜,因此这只是我们认为该功能是否重要的问题。
就像我经常说的那样,将浮点运算结果四舍五入到可表示的最近值(或任何舍入方向)的事实并不是浪费ulp的许可。精确性在运行时损失和实现复杂性方面都有代价,因此需要权衡取舍。
答案 7 :(得分:1)
python中数字的repr
表示(float.__repr__
)在转换回来时会尝试返回尽可能接近实际值的数字字符串,因为IEEE-754算术是精确的达到极限。在任何情况下,如果你print
编辑了结果,你就不会注意到:
>>> from math import log
>>> log(2**31,2)
31.000000000000004
>>> print log(2**31,2)
31.0
print
将其参数转换为字符串(在这种情况下,通过float.__str__
方法),通过显示更少的数字来满足不准确性:
>>> log(1000000,2)
19.931568569324174
>>> print log(1000000,2)
19.9315685693
>>> 1.0/10
0.10000000000000001
>>> print 1.0/10
0.1
通常无用'答案非常有用,实际上:)
答案 8 :(得分:0)
如果要计算数字“ n”中的最高功率“ k”。那么下面的代码可能会有所帮助:
import math
answer = math.ceil(math.log(n,k))
while k**answer>n:
answer-=1
注意::您不应该使用“ if”而不是“ while”,因为在某些情况下,例如n = 2 ** 51-1和k = 2,这将导致错误的结果。在此示例中,如果答案为“ if”,则答案为51,而答案为“ while”时,答案为50,这是正确的。