Python是否有类似java.lang.Math.nextUp的方法?

5 投票
2 回答
1724 浏览
提问于 2025-04-16 16:12

我有一个Python中的float(浮点数),我想得到比这个浮点数大1个ULP(单位在最后一位)和小1个ULP的浮点数。

在Java中,我可以用Math.nextUp(x)来获取比x大的下一个浮点数,使用Math.nextAfter(x, Double.NEGATIVE_INFINITY)来获取比x小的下一个浮点数。

那么在Python中有没有类似的方法呢?我考虑过用math.frexpmath.ldexp自己实现,但我知道Python并没有规定浮点数的大小。

2 个回答

1

我不确定这是不是你想要的,但 sys.float_info.epsilon 是“1和能表示为浮点数的比1稍大的最小值之间的差”,你可以这样做 x * (1 + sys.float_info.epsilon)

http://docs.python.org/library/sys.html#sys.float_info

5

更新: 在 Python 3.9 及以上版本中,有一个新的功能 math.nextafter()

>>> import math
>>> x = 4
>>> math.nextafter(x, math.inf)
4.000000000000001

旧答案:

你可以看看 Decimal.next_plus()/Decimal.next_minus() 是怎么实现的:

>>> 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')

确保 小数上下文 中有你需要的值:

>>> from decimal import getcontext
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999,
capitals=1, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])

其他选择:

  • 使用 C99 的 nextafter(),通过 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)
    

    使用 numpy

      >>> import numpy
      >>> numpy.nextafter(4, float('+inf'))
      4.0000000000000009
      >>> _.as_integer_ratio()
      (4503599627370497, 1125899906842624)
    

    尽管 repr() 的表现不同,但结果是一样的。

  • 如果忽略一些特殊情况,那么来自 @S.Lott 的答案 中的简单 frexp/ldexp 方法可以工作:

      >>> 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)
    
  • @Mark Dickinson 提供的纯 Python next_after(x, y) 实现,考虑了特殊情况。在这种情况下,结果是一样的。

撰写回答