Python中不准确的对数

Python中不准确的对数,python,math,floating-point,Python,Math,Floating Point,我在公司每天使用Python 2.4。我使用了标准数学库中的通用对数函数“log”,当我输入log(2**31,2)时,它返回31.000000000000004,这让我觉得有点奇怪 我用2的其他幂做了同样的事情,它工作得很好。我跑了“log10(2**31)/log10(2)”,得到了一轮31.0分 我尝试在Python3.0.1中运行相同的原始函数,假设它在更高级的版本中得到修复 为什么会发生这种情况?Python中的数学函数是否可能存在一些不准确的地方?这是计算机算术所期望的。它遵循特定的

我在公司每天使用Python 2.4。我使用了标准数学库中的通用对数函数“log”,当我输入log(2**31,2)时,它返回31.000000000000004,这让我觉得有点奇怪

我用2的其他幂做了同样的事情,它工作得很好。我跑了“log10(2**31)/log10(2)”,得到了一轮31.0分

我尝试在Python3.0.1中运行相同的原始函数,假设它在更高级的版本中得到修复


为什么会发生这种情况?Python中的数学函数是否可能存在一些不准确的地方?

这是计算机算术所期望的。它遵循特定的规则,例如,可能与你在学校学到的数学不匹配的规则

如果这真的很重要,请使用Python的

例如:

from decimal import Decimal, Context
ctx = Context(prec=20)
two = Decimal(2)
ctx.divide(ctx.power(two, Decimal(31)).ln(ctx), two.ln(ctx))
始终假定浮点操作中会有一些错误,并在考虑该错误的情况下检查是否相等(百分比值(如0.00001%)或固定值(如0.0000000000 1)。由于并非所有十进制数都能以固定位数精度的二进制表示,因此这种不精确性是已知的


如果Python使用IEEE754,那么您的特殊情况就不是其中之一,因为IEEE754应该可以很容易地以单精度表示。然而,在计算log2231所需的许多步骤中,有一步可能会丢失精度,原因很简单,因为它没有检测特殊情况(如二的直接幂)的代码。

浮点运算永远都不精确。对于语言/硬件基础结构,它们返回的结果具有可接受的相对误差


一般来说,假设浮点运算是精确的是非常错误的,尤其是单精度运算。来自维基百科的浮点文章:)

这是正常的。我希望log10比log(x,y)更精确,因为它确切地知道对数的底是什么,也可能有一些硬件支持来计算以10为底的对数。

你应该阅读“每个计算机科学家都应该知道的浮点算术”


IEEE双浮点数已经过测试。由于10^15<2^52<10^16,双精度的有效数字在15到16之间。结果31.000000000000004正确到16位,因此它与您所期望的一样好。

python中数字的
repr
表示(
float.\uuuuu repr\uuuuu
)尝试在转换回时返回尽可能接近真实值的数字字符串,因为IEEE-754算法的精度达到了极限。在任何情况下,如果打印结果,您都不会注意到:

>>> from math import log
>>> log(2**31,2)
31.000000000000004
>>> print log(2**31,2)
31.0
print
将其参数转换为字符串(在本例中,通过
float.\uuuu str\uuuu
方法),该方法通过显示较少的数字来解决不准确问题:

>>> log(1000000,2)
19.931568569324174
>>> print log(1000000,2)
19.9315685693
>>> 1.0/10
0.10000000000000001
>>> print 1.0/10
0.1
事实上,通常你的回答很有用:)

浮点数是不精确的

我不相信这种说法,因为在大多数平台上(使用底层的IEEE 754浮点),2的精确幂都是精确表示的

所以,如果我们真的想要2的精确幂的log2是精确的,我们可以。
我将在Squeak Smalltalk中演示它,因为在该语言中更改基本系统很容易,但该语言并不重要,浮点计算是通用的,Python对象模型离Smalltalk也不远

对于以n为底的log,有一个在Number中定义的log:函数,它简单地使用Neperian对数
ln

log: aNumber 
    "Answer the log base aNumber of the receiver."
    ^self ln / aNumber ln
self ln
(取接收器的尼伯耳对数)、
aNumber ln
/
是将结果四舍五入到最近的浮点值的三种操作,这些四舍五入误差会累积。。。因此,naive实现会受到您观察到的舍入误差的影响,我猜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
在数字类中提供通用log2:

log2
    "Answer the base-2 log of the receiver."
    ^self asFloat log2
这是浮动类的补给:

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
是一个常数(2ln),那么我有效地得到了2的精确幂的精确log2,因为这样的数字的有效位=1.0(包括尖叫指数/有效位定义的次正常值),并且
1.0ln=0.0

实现非常简单,在Python中(可能在VM中)翻译应该不会有困难;运行时成本非常便宜,所以这只是我们认为这个特性有多重要的问题


正如我经常说的,浮点运算结果被四舍五入到最接近(或任何四舍五入方向)的表示值这一事实并不是浪费ulp的许可证。精确性在运行时惩罚和实现复杂性方面都有成本,因此它是权衡驱动的。

如果您希望计算数字“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,这会给出错误的结果。在这个例子中,“如果”的答案是51,而“while”的答案是50,这是正确的。

+1是一个很好的答案,但主要是“如果这真的很重要。”探测器飞到土星的精度较低。的确如此。斜体是答案中最重要的部分。@dwc如果OP一直在记录log函数的结果,那就很重要了。然后误差会变得非常大。在我的例子中,在我的一个程序中,我是这样做的:
a=floor(log(x,b))
程序在前面几行崩溃了,因为
floor(log(243,3))
是永久性浮点问题的4倍(为什么我会出现浮点错误?),找不到最好的重复Q来发布,也许其他人可以。我应该指出,Python3没有修复浮点错误。相反,打印输出使用智能算法来显示