浮点数是不精确的。
我不认同这个观点,因为在大多数平台上,精确的二次幂可以被准确地表示(使用底层IEEE 754浮点数)。
因此,如果我们真的希望一个精确的2的对数是准确的,我们就可以做到。
我将在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的操作,这些舍入误差可能会累积...因此,天真的实现会受到您观察到的舍入误差的影响,我猜想Python的log函数实现也不会有太大的区别。
((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,因为这种数字的有效数字=1.0(包括Squeak指数/有效数字定义的子规范),而1.0 ln = 0.0
。
实现非常简单,并且应该在Python中很容易转换(可能在VM中);运行时成本非常便宜,因此只是一个我们认为这个功能有多重要或不重要的问题。
正如我经常说的那样,浮点运算结果舍入为最接近的(或任何舍入方向)可表示值并不意味着我们可以浪费ulp。精确性具有成本,无论是运行时惩罚还是实现复杂性,都需要权衡。