如何获得浮点序列中的下一个值?

时间:2012-05-02 20:05:42

标签: python math floating-point

Python是否提供了一个函数来获取由于递增现有浮点值的最低有效位而产生的浮点值?

我正在寻找与C ++ 11中添加的std::nextafter函数类似的东西。

3 个答案:

答案 0 :(得分:17)

回答问题的第一部分:不,Python不直接提供此功能。但是编写一个Python函数很容易做到这一点,假设IEEE 754是浮点数。

IEEE 754二进制浮点格式设计得非常巧妙,因此从一个浮点数移动到“下一个”浮点数就像递增位表示一样简单。这适用于[0, infinity)范围内的任何数字,跨越指数边界和次正规数。要生成涵盖完整浮点范围的nextUp版本,您还需要处理负数,无穷大,nans和一个涉及负零的特殊情况。以下是Python中IEEE 754 nextUp函数的标准兼容版本。它涵盖了所有角落案例。

import math
import struct

def next_up(x):
    # NaNs and positive infinity map to themselves.
    if math.isnan(x) or (math.isinf(x) and x > 0):
        return x

    # 0.0 and -0.0 both map to the smallest +ve float.
    if x == 0.0:
        x = 0.0

    n = struct.unpack('<q', struct.pack('<d', x))[0]
    if n >= 0:
        n += 1
    else:
        n -= 1
    return struct.unpack('<d', struct.pack('<q', n))[0]

nextDownnextAfter的实现看起来像这样。 (注意nextAfter不是IEEE 754指定的函数,因此对于IEEE特殊值应该发生什么有一些猜测。这里我遵循Python的{{1}的IBM Decimal Arithmetic标准。 } class基于。)

decimal.Decimal

答案 1 :(得分:2)

<强>更新

原来这是一个重复的问题(谷歌出现在搜索“c ++ nextafter python”的结果#2中):Increment a python floating point value by the smallest possible amount

接受的答案提供了一些可靠的解决方案。

原始答案:

当然这不是完美的解决方案,但使用cython只需几行就可以包装现有的C ++函数并在Python中使用它。我编译了下面的代码,它可以在我的ubuntu 11.10框上运行。

首先,.pyx文件(我称之为nextafter.pyx)定义了C ++的接口:

cdef extern from "cmath":
    float nextafter(float start, float to)

def pynextafter(start, to):
    cdef float float_start = float(start)
    cdef float float_to = float(to)
    result = nextafter(start, to)
    return result

然后setup.py定义了如何构建扩展:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext 

ext_modules=[
    Extension("nextafter",
        ["nextafter.pyx"],
        libraries=[],
        library_dirs=[],
        include_dirs=[],
        language="c++",
    )
]

setup(
    name = "nextafter",
    cmdclass = {"build_ext": build_ext},
    ext_modules = ext_modules
)

确保它们位于同一目录中,然后使用python setup.py build_ext --inplace进行构建。我希望你能看到如何将nextafter的其他变体添加到扩展名中(对于双打等)。一旦建成,你应该有一个nextafter.so。在同一目录中启动python(或在你的路径上放置nextafter.so),你应该可以调用from nextafter import pynextafter

享受!

答案 2 :(得分:1)

查看http://docs.python.org/library/stdtypes.html#float.hex

让我们试试这个对下一个不太了解的实现。

首先,我们需要从十六进制字符串中提取十六进制部分和指数:

def extract_parts(hex_val):
    if not hex_val.startswith('0x1.'):
        return None
    relevant_chars = hex_val[4:]
    if not len(relevant_chars) > 14 and relevant_chars[13] == 'p':
        return None
    hex_portion = int(relevant_chars[:13], 16)
    if relevant_chars[14] == '+':
        p_val = int(relevant_chars[15:])
    elif relevant_chars[14] == '-':
        p_val = -int(relevant_chars[15:])
    else:
        return None
    return (hex_portion, p_val)

然后我们需要一种在正方向或负方向上递增的方法(我们假设十六进制字符串已经转换为整数hex_portion):

def increment_hex(hex_portion, p_val, direction):
    if hex_portion == 0 and direction == -1:
        new_hex = 'f' * 13
        p_val -= 1
    elif hex_portion == int('f' * 13, 16) and direction == 1:
        new_hex = '0' * 13
        p_val += 1
    else:
        new_hex = hex(hex_portion + direction)[2:].rstrip('L').zfill(13)

    if len(new_hex) != 13:
        return None
    return format_hex(new_hex, p_val)

我们需要一个辅助函数来重新格式化我在上面使用的可接受的十六​​进制字符串和指数:

def format_hex(hex_as_str, p_val):
    sign = '-' if p_val < 0 else '+'
    return '0x1.%sp%s%d' % (hex_as_str, sign, p_val)

最后,实施nextafter

def nextafter(float_val):
    hex_equivalent = float_val.hex()
    hex_portion, p_val = extract_parts(hex_equivalent)

    direction = 1
    new_hex_equiv = increment_hex(hex_portion, p_val, direction)
    return float.fromhex(new_hex_equiv)