可能有'a&lt; b而不是(a - b <0)``浮点数

时间:2014-02-05 23:28:43

标签: python floating-point

a < b and not(a - b < 0)

由于浮点轮误差可能导致浮点数? 有一个例子吗?

3 个答案:

答案 0 :(得分:10)

[这个答案是作为学者对Patricia Shanahan已经给出的正确答案的补充。答案涵盖了正常情况;在这里,我们担心你在实践中不太可能遇到的边缘情况。]

是的,这是完全可能的。这是我非常普通的基于Intel的Mac笔记本电脑的Python会话:

Enthought Canopy Python 2.7.6 | 64-bit | (default, Jan 29 2014, 17:09:48) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import evil
>>> a = float.fromhex('1p-1022')
>>> b = float.fromhex('1.01p-1022')
>>> a < b and not(a - b < 0)
True

当然,正如进口所表明的,这里有一些邪恶的东西。以下是evil.py的内容。 (警告:这是高度特定于平台的,并且编写的内容仅适用于OS X.如果已编译Python以使用SSE2指令而不是x87单元进行浮点运算,则此变体应适用于Linux / x64我不知道Windows。)

# Being lazy: we should really import just the things we need.
from ctypes import *

# Type corresponding to fenv_t from fenv.h.  (Platform specific!)
class fenv_t(Structure):
    _fields_ = [("control", c_ushort), ("status", c_ushort),
                ("mxcsr", c_uint), ("reserved", c_char * 8)]

# Wrap fegetenv and fesetenv from the C library.
libc = CDLL("/usr/lib/libc.dylib")
fegetenv = libc.fegetenv
fegetenv.restype, fegetenv.argtypes = c_int, (POINTER(fenv_t),)
fesetenv = libc.fesetenv
fesetenv.restype, fesetenv.argtypes = c_int, (POINTER(fenv_t),)

# Set the flush-to-zero (FTZ) bit in the MXCSR control register.
env = fenv_t()
fegetenv(pointer(env))
env.mxcsr |= 1 << 15
fesetenv(pointer(env))

所以我们在这里做的是弄乱MXCSR控制寄存器中保存的FPU设置。在支持SSE指令集的Intel 64处理器上,有两个有趣的标志会影响涉及次正规数的操作行为。 FTZ(清零到零)标志置位时,会将算术运算的任何次正常输出替换为零。 DAZ(denormals-are-zero)标志置位时,会将算术运算的任何次正常输入视为零。这些标志的重点在于它们可以显着加速涉及次正规数的操作,但代价是牺牲了对IEEE 754标准的符合性。在上面的代码中,我们在MXCSR控制寄存器中设置了FTZ标志。

现在我们选择ab,以便ab都正常,但它们的差异是次正常的。然后a < b将为真(如常),但a - b将为-0.0,而比较a - b < 0将失败。

外卖点是,您使用的浮点格式是IEEE 754格式是不够的。您还需要知道您的操作符合标准。 FTZ模式是可能失败的一种方式的示例。将此与Patricia Shanahan的回答联系起来:evil.py中的代码正在关闭IEEE 754承诺的逐渐下溢。 (感谢@EricPostpischil在评论中指出这一点。)

答案 1 :(得分:9)

它取决于浮点格式,特别是它是否具有gradual underflow。 IEEE 754二进制浮点确实如此。这意味着两个不同浮点之间的间隙总是非零,即使在极端情况下它只能用一个有效位表示。

两个不同浮点数之间的最小可能绝对差值带有等号(或数字零之一),零指数和有效数相差1.在这种情况下,从较小值中减去较大值将导致最小值负数,负号,零指数和有效数1.该数字小于零。

还有其他计算下溢到零。例如,将最小正数除以大于或等于2的数字会导致正常舍入模式为零。

答案 2 :(得分:0)

你可以像这样轻松地完成一些“极端案例”

>>> from itertools import product
>>> for a, b in product([0.0, 1.0, float('-inf'), float('inf'), float('nan')], repeat=2):
...     print a < b and not(a - b < 0)... 
False
False
# and so on