关于数学:Python中不准确的对数

Inaccurate Logarithm in Python

我每天和公司的python 2.4一起工作。我使用了标准数学库中的通用对数函数"log",当我输入log(2**31,2)时,它返回31.00000000000004,这让我觉得有点奇怪。

我用2的其他力量也做了同样的事情,而且效果很好。我运行了"log10(2**31)/log10(2)",得到了第31.0轮。

我尝试在python 3.0.1中运行相同的原始函数,假设它是在更高级的版本中修复的。

为什么会这样?在python中,数学函数是否可能有些不准确?


这是计算机算术的预期结果。它遵循特定的规则,比如IEEE754,可能与你在学校学到的数学不匹配。

如果这真的很重要,请使用python的decimal类型。

例子:

1
2
3
4
from decimal import Decimal, Context
ctx = Context(prec=20)
two = Decimal(2)
ctx.divide(ctx.power(two, Decimal(31)).ln(ctx), two.ln(ctx))


你应该读"每个计算机科学家都应该知道什么是浮点运算"。

http://docs.sun.com/source/806-3568/ncg_goldberg.html


始终假定浮点运算中会有一些错误,并在考虑该错误的情况下检查是否相等(百分比值如0.00001%或固定值如0.00000000001)。这种不精确性是给定的,因为并非所有的十进制数都可以用固定位数的位精度表示为二进制数。

如果Python使用IEEE754,那么您的特定情况就不是其中之一了,因为31应该很容易表示,即使只有一个精度。但是,它可能会在计算log 22231所需的许多步骤中失去精度,这仅仅是因为它没有代码来检测特殊情况,如直接的2次幂。


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

一般来说,假设浮点运算是精确的,特别是单精度的,这是非常错误的。"准确度问题"来自维基百科浮点文章的章节:)


IEEE双浮点数的精度为52位。由于10^15<2^52<10^16,双精度数的有效数字介于15和16之间。结果31.00000000000004是正确的16位数字,所以它是您可以预期的好。


float are imprecise

我不相信这个论点,因为两个的确切力量在大多数平台上都是精确地表示的(有基础的IEEE754浮点)。

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

对于以n为底的对数,有以number定义的log:函数,它幼稚地使用了尼泊尔对数ln

1
2
3
log: aNumber
   "Answer the log base aNumber of the receiver."
    ^self ln / aNumber ln

self ln(取接收机的尼泊尔对数)、aNumber ln/是三种将其四舍五入到最近的浮点的运算,这些四舍五入误差可以累计…所以幼稚的实现会受到您观察到的舍入错误的影响,我想日志函数的python实现也没有太大的不同。

1
((2 raisedTo: 31) log: 2) = 31.000000000000004

但是如果我像这样改变定义:

1
2
3
4
log: aNumber
   "Answer the log base aNumber of the receiver."
    aNumber = 2 ifTrue: [^self log2].
    ^self ln / aNumber ln

在数字类中提供一个通用的log2:

1
2
3
log2
   "Answer the base-2 log of the receiver."
    ^self asFloat log2

这是浮点数级的改进:

1
2
3
4
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),那么我有效地得到一个精确的log2,精确的二次幂,因为这个数的有效位=1.0(包括squak指数/有效位定义的次正规)和1.0 ln = 0.0

实现非常简单,应该在python(可能在vm中)中毫无困难地进行转换;运行时成本非常低廉,所以这只是我们认为这个特性有多重要或没有多重要的一个问题。

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


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


在python中,一个数字的repr表示(float.__repr__)试图返回一个尽可能接近实际值的数字串,因为ieee-754算法精确到极限。在任何情况下,如果您确认了结果,您不会注意到:

1
2
3
4
5
>>> from math import log
>>> log(2**31,2)
31.000000000000004
>>> print log(2**31,2)
31.0

print将其参数转换为字符串(在本例中,通过float.__str__方法),该方法通过显示较少的数字来解决不准确的问题:

1
2
3
4
5
6
7
8
>>> log(1000000,2)
19.931568569324174
>>> print log(1000000,2)
19.9315685693
>>> 1.0/10
0.10000000000000001
>>> print 1.0/10
0.1

通常你的回答是非常有用的,事实上:)