Увеличьте значение с плавающей запятой Python на наименьшую возможную сумму
Вы не сумасшедший, и вы должны быть в состоянии это сделать.К сожалению, это текущий недостаток математической библиотеки Python как в Python 2.X, так и в Python3000.В Python должно быть math.nextafter(x,y)
, но его нет.Было бы тривиально добавить, так как большинство компиляторов Си имеют функции.
Функции nextafter (x, y) возвращают следующее дискретно другое представимое значение с плавающей точкой, следующее за x в направлении y.Функции nextafter () гарантированно работают на платформе или возвращают разумное значение, чтобы указать, что следующее значение невозможно.
Функции nextafter()
являются частью стандартов POSIX и ISO C99 и _nextafter () в Visual C .Стандартные математические библиотеки, совместимые с C99, Visual C, C ++, Boost и Java все реализуют рекомендуемые IEEE функции или методы nextafter ().(Я честно не знаю, есть ли в .NET nextafter (). Microsoft не особо заботится о C99 или POSIX.)
Поскольку Python, похоже, движется в направлении поддержки большинства математических функций и поведений C99 дляматематический модуль, исключение nextafter()
любопытно.К счастью, есть простые обходные пути.
Ни одна из функций перестановки битов здесь полностью или правильно не обрабатывает граничные случаи, такие как значения, проходящие через 0,0, отрицательные 0,0, субнормальные, бесконечности, отрицательные значения, переполнения или недостаточные значения и т. Д.. Вот эталонная реализация nextafter () в C , чтобы дать представление о том, как выполнить правильное переключение битов, если это ваше направление.
Есть два надежных обходных пути для получения nextafter()
или других исключенных математических функций POSIX в Python:
Использование 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)
Или, используйте Марк Дикинсон превосходное решение
Очевидно, что решение Numpy является самым простым.