2011-04-21 11 views
5

Tengo un Python float, y quiero que el float s que son 1 ULP mayor y menor.¿Tiene Python un equivalente a java.lang.Math.nextUp?

En Java, haría esto con Math.nextUp(x) y Math.nextAfter(x, Double.NEGATIVE_INFINITY).

¿Hay alguna manera de hacer esto en Python? Pensé en implementarlo yo mismo con math.frexp and math.ldexp pero, por lo que sé, Python no especifica el tamaño de los tipos de coma flotante.

+1

La misma pregunta aquí: http://bytes.com/topic/python/answers/739926-next-float, con buenas respuestas ... – Benjamin

Respuesta

1

No estoy seguro de si esto es lo que quiere, pero sys.float_info.epsilon es la "diferencia entre 1 y el valor menor que 1 representable como un flotador", y podría hacer x * (1 + sys.float_info.epsilon).

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

+1

Es una idea relacionada, pero no es exactamente lo que quiero . Esencialmente es correcto para un exponente de 0, pero para exponentes más pequeños es demasiado grande, y para más grande es demasiado pequeño. Gracias por señalar 'sys.float_info'. Al menos con esta estructura puedo determinar de manera confiable el tamaño del tipo de punto flotante subyacente, incluso si todavía tengo que adivinar que es IEEE-754. Con esto puedo encontrar una solución siguiendo las sugerencias de @ Benjamin, específicamente con el módulo struct. – rlibby

+0

'x + epsilon'? ¿no sería más bien 'x * (1 + epsilon)'? – vartec

+0

@vartec No lo sé, pero lo corregí en la respuesta. –

2

Se podía ver cómo se implementan Decimal.next_plus()/Decimal.next_minus():

>>> from decimal import Decimal as D 
>>> d = D.from_float(123456.789) 
>>> d 
Decimal('123456.789') 
>>> d.next_plus() 
Decimal('123456.789') 
>>> d.next_minus() 
Decimal('123456.789') 
>>> d.next_toward(D('-inf')) 
Decimal('123456.789') 

Asegúrese de que decimal context tiene valores que necesitará:

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

Alternativas:

  • Call C99 nextafter() usando 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) 
    

    Usando numpy:

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

    pesar de las diferentes repr(), el resultado es el mismo.

  • si ignoramos casos extremos a continuación, una solución frexp/ldexp simple a partir de @S.Lott answer obras:

    >>> 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 que tenga en cuenta los casos límite. El resultado es el mismo en este caso. Se preguntó

+0

Se ve bien por necesitar algo de brillo, ya que float en python tiene 53 bits para mantissa http: //docs.python.org/2/tutorial/floatingpoint.html. Creo que es mejor utilizar frexp ldexp, puede ser más rápido. – Chameleon

+0

@Chameleon: sí. Puede usar 'frexp()'/'ldexpr()' para encontrar el "siguiente después". – jfs

Cuestiones relacionadas