¿Cómo se obtiene el siguiente valor en la secuencia de punto flotante?

¿Python proporciona una función para obtener el valor de punto flotante que resulta de incrementar el bit menos significativo de un valor de punto flotante existente?

Estoy buscando algo similar a la función std::nextafter que se agregó en C ++ 11.

Para responder la primera parte de su pregunta: no, Python no proporciona esta funcionalidad directamente. Pero es bastante fácil escribir una función de Python que hace esto, asumiendo el punto flotante IEEE 754.

Los formatos binarios de punto flotante IEEE 754 están diseñados de forma bastante inteligente, de modo que pasar de un número de punto flotante al “siguiente” es tan simple como incrementar la representación de bits. Esto funciona para cualquier número en el rango [0, infinity) , a través de los límites de exponentes y subnormales. Para producir una versión de nextUp que cubra el rango completo de punto flotante, también debe tratar con números negativos, infinitos, nans y un caso especial que involucre un cero negativo. A continuación se muestra una versión compatible con los estándares de la función nextUp de IEEE 754 en Python. Cubre todos los casos de la esquina.

 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('= 0: n += 1 else: n -= 1 return struct.unpack(' 

Las implementaciones de nextDown y nextAfter tienen este aspecto. (Tenga en cuenta que nextAfter no es una función especificada por IEEE 754, por lo que hay nextAfter conjeturas sobre lo que debería suceder con los valores especiales de IEEE. Aquí estoy siguiendo el estándar de IBM Decimal Arithmetic en el que se decimal.Decimal clase decimal.Decimal de Python. )

 def next_down(x): return -next_up(-x) def next_after(x, y): # If either argument is a NaN, return that argument. # This matches the implementation in decimal.Decimal if math.isnan(x): return x if math.isnan(y): return y if y == x: return y elif y > x: return next_up(x) else: return next_down(x) 

ACTUALIZAR:

Resulta que esta es una pregunta duplicada (que aparece en google como resultado # 2 para la búsqueda “c ++ nextafter python”): Incrementa el valor de punto flotante de python en la menor cantidad posible

La respuesta aceptada proporciona algunas soluciones sólidas.

RESPUESTA ORIGINAL:

Ciertamente, esta no es la solución perfecta, pero usar cython solo unas pocas líneas le permitirá envolver la función de C ++ existente y usarla en Python. He comstackdo el siguiente código y funciona en mi caja de ubuntu 11.10.

Primero, un archivo .pyx (que llamé mío nextafter.pyx) define su interfaz con 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 

Luego, un setup.py define cómo construir la extensión:

 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 ) 

Asegúrese de que estén en el mismo directorio y luego compile con python setup.py build_ext --inplace . Espero que pueda ver cómo agregaría las otras variaciones de nextafter a la extensión (para dobles, etc …). Una vez construido, deberías tener un nextafter.so. Inicie python en el mismo directorio (o coloque nextafter.so en su ruta en algún lugar) y debería poder llamar from nextafter import pynextafter .

¡Disfrutar!

Echa un vistazo a http://docs.python.org/library/stdtypes.html#float.hex

Probemos esta implementación que no se sabe mucho sobre el siguiente.

Primero, necesitamos extraer la parte hexagonal y el exponente de la cadena 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) 

Luego necesitamos una forma de incrementar en dirección positiva o negativa (asumiremos que la cadena hexadecimal ya se ha convertido en una hex_portion entera):

 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) 

Necesitamos una función auxiliar para reformatear una cadena y un exponente hexadecimales aceptables, que utilicé anteriormente:

 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) 

Finalmente, para implementar 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)