从 Python 3.9 开始,stdlib 中有 math.nextafter。继续阅读旧 Python 版本中的替代方案。
将python浮点值增加最小的可能量
nextafter(x,y) 函数返回在 y 方向上跟随 x 的下一个离散不同的可表示浮点值。 nextafter() 函数保证在平台上工作或返回一个合理的值以指示下一个值是不可能的。
nextafter() 函数是 POSIX 和 ISO C99 标准的一部分,并且是 _nextafter() in Visual C。符合 C99 标准的数学库、Visual C、C++、Boost 和 Java 都实现了 IEEE 推荐的 nextafter() 函数或方法。 (老实说,我不知道 .NET 是否有 nextafter()。微软不太关心 C99 或 POSIX。)
没有位旋转函数完全或正确处理边缘情况,例如值通过 0.0、负 0.0、次正规、无穷大、负值、上溢或下溢等。@ 987654326@ 给出一个想法,如果这是你的方向,如何做正确的位旋转。
有两个可靠的解决方法可以在 Python nextafter() 或其他排除的 POSIX 数学函数:
使用 Numpy:
>>> import numpy
>>> numpy.nextafter(0,1)
4.9406564584124654e-324
>>> numpy.nextafter(.1, 1)
0.10000000000000002
>>> numpy.nextafter(1e6, -1)
999999.99999999988
>>> numpy.nextafter(-.1, 1)
-0.099999999999999992
直接链接到系统数学 DLL:
import ctypes
import sys
from sys import platform as _platform
if _platform == "linux" or _platform == "linux2":
_libm = ctypes.cdll.LoadLibrary('libm.so.6')
_funcname = 'nextafter'
elif _platform == "darwin":
_libm = ctypes.cdll.LoadLibrary('libSystem.dylib')
_funcname = 'nextafter'
elif _platform == "win32":
_libm = ctypes.cdll.LoadLibrary('msvcrt.dll')
_funcname = '_nextafter'
else:
# these are the ones I have access to...
# fill in library and function name for your system math dll
print("Platform", repr(_platform), "is not supported")
sys.exit(0)
_nextafter = getattr(_libm, _funcname)
_nextafter.restype = ctypes.c_double
_nextafter.argtypes = [ctypes.c_double, ctypes.c_double]
def nextafter(x, y):
"Returns the next floating-point number after x in the direction of y."
return _nextafter(x, y)
assert nextafter(0, 1) - nextafter(0, 1) == 0
assert 0.0 + nextafter(0, 1) > 0.0
如果你真的想要一个纯 Python 解决方案:
# handles edge cases correctly on MY computer
# not extensively QA'd...
import math
# 'double' means IEEE 754 double precision -- c 'double'
epsilon = math.ldexp(1.0, -53) # smallest double that 0.5+epsilon != 0.5
maxDouble = float(2**1024 - 2**971) # From the IEEE 754 standard
minDouble = math.ldexp(1.0, -1022) # min positive normalized double
smallEpsilon = math.ldexp(1.0, -1074) # smallest increment for doubles < minFloat
infinity = math.ldexp(1.0, 1023) * 2
def nextafter(x,y):
"""returns the next IEEE double after x in the direction of y if possible"""
if y==x:
return y #if x==y, no increment
# handle NaN
if x!=x or y!=y:
return x + y
if x >= infinity:
return infinity
if x <= -infinity:
return -infinity
if -minDouble < x < minDouble:
if y > x:
return x + smallEpsilon
else:
return x - smallEpsilon
m, e = math.frexp(x)
if y > x:
m += epsilon
else:
m -= epsilon
return math.ldexp(m,e)
或者,使用Mark Dickinson's优秀solution
显然Numpy 解决方案是最简单的。