Does Python have an equivalent to java.lang.Math.nextUp?

floating-point, python

Solution

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.

Problem

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.

Original source

Related problems