I have a Python float
, and I want to have the float
s which are 1 ULP greater and lesser.
In Java, I would do this with Math.nextUp(x)
and Math.nextAfter(x, Double.NEGATIVE_INFINITY)
.
Is there a way to do this in Python? I thought about implementing it myself with math.frexp
and math.ldexp
but as far as I know Python doesn't specify the size of floating point types.
Update: In Python 3.9+ there is math.nextafter()
:
>>> import math
>>> x = 4
>>> math.nextafter(x, math.inf)
4.000000000000001
Old answer:
You could look at how Decimal.next_plus()
/Decimal.next_minus()
are implemented:
>>> from decimal import Decimal as D
>>> d = D.from_float(123456.78901234567890)
>>> d
Decimal('123456.789012345674564130604267120361328125')
>>> d.next_plus()
Decimal('123456.7890123456745641306043')
>>> d.next_minus()
Decimal('123456.7890123456745641306042')
>>> d.next_toward(D('-inf'))
Decimal('123456.7890123456745641306042')
Make sure that decimal context has values that you need:
>>> from decimal import getcontext
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999,
capitals=1, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
Alternatives:
Call C99 nextafter()
using ctypes
:
>>> import ctypes
>>> nextafter = ctypes.CDLL(None).nextafter
>>> nextafter.argtypes = ctypes.c_double, ctypes.c_double
>>> nextafter.restype = ctypes.c_double
>>> nextafter(4, float('+inf'))
4.000000000000001
>>> _.as_integer_ratio()
(4503599627370497, 1125899906842624)
Using numpy
:
>>> import numpy
>>> numpy.nextafter(4, float('+inf'))
4.0000000000000009
>>> _.as_integer_ratio()
(4503599627370497, 1125899906842624)
Despite different repr()
, the result is the same.
If we ignore edge cases then a simple frexp/ldexp solution from @S.Lott answer works:
>>> import math, sys
>>> m, e = math.frexp(4.0)
>>> math.ldexp(2 * m + sys.float_info.epsilon, e - 1)
4.000000000000001
>>> _.as_integer_ratio()
(4503599627370497, 1125899906842624)
pure Python next_after(x, y)
implementation by @Mark Dickinson that takes into account edge cases. The result is the same in this case.
I am not sure if this is what you want, but sys.float_info.epsilon
is the "difference between 1 and the least value greater than 1 that is representable as a float", and you could do x * (1 + sys.float_info.epsilon)
.
http://docs.python.org/library/sys.html#sys.float_info
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With