Función para determinar si dos números son prácticamente iguales cuando redondeado a n dígitos decimales significativos

Me han pedido para probar una biblioteca provista por una 3 ª parte. La biblioteca es conocido para ser exactos n cifras significativas. Menos significativo de los errores de seguridad puede ser ignorado. Quiero escribir una función para que me ayude a comparar los resultados:

def nearlyequal( a, b, sigfig=5 ):

El propósito de esta función es determinar si dos números en punto flotante (a y b) son aproximadamente iguales. La función devolverá True si a==b (coincidencia exacta) o si a y b tienen el mismo valor cuando se redondea a sigfig importantes figuras escrito en decimal.

¿Alguien puede sugerir una buena aplicación? He escrito una mini unidad de prueba. A menos que usted puede ver un error en mis pruebas, a continuación, una buena aplicación debe pasar los siguientes:

assert nearlyequal(1, 1, 5) 
assert nearlyequal(1.0, 1.0, 5) 
assert nearlyequal(1.0, 1.0, 5) 
assert nearlyequal(-1e-9, 1e-9, 5) 
assert nearlyequal(1e9, 1e9 + 1 , 5) 
assert not nearlyequal( 1e4, 1e4 + 1, 5) 
assert nearlyequal( 0.0, 1e-15, 5 ) 
assert not nearlyequal( 0.0, 1e-4, 6 ) 

Notas adicionales:

  1. Los valores de a y b pueden ser de tipo int, float o numpy.float64. Los valores de a y b siempre será del mismo tipo. Es vital que la conversión no introduce error adicional en la función.
  2. Permite mantener este numérica, así que las funciones que se convierten en cadenas de utilizar o no-matemático trucos no son los ideales. Este programa podrán ser auditadas por alguien que es un matemático que se quiere ser capaz de demostrar que la función hace lo que se supone que debe hacer.
  3. Velocidad… tengo que comparar un montón de números, por lo que cuanto más rápido mejor.
  4. Tengo numpy, scipy y el estándar de la biblioteca. Cualquier otra cosa va a ser difícil para mí para conseguir, especialmente para una pequeña parte del proyecto.

OriginalEl autor Salim Fadhley | 2009-02-17

11 respuestas

  1. 18

    Hay una función assert_approx_equal en numpy.testing (fuente aquí) que puede ser un buen punto de partida.

    def assert_approx_equal(actual,desired,significant=7,err_msg='',verbose=True):
        """
        Raise an assertion if two items are not equal up to significant digits.
    
        .. note:: It is recommended to use one of `assert_allclose`,
                  `assert_array_almost_equal_nulp` or `assert_array_max_ulp`
                  instead of this function for more consistent floating point
                  comparisons.
    
        Given two numbers, check that they are approximately equal.
        Approximately equal is defined as the number of significant digits
        that agree.
    unittest.assertAlmostEqual() me deje engañar por ignorar los dígitos insignificantes.

    OriginalEl autor dF.

  2. 41

    Como de Python 3.5, la manera estándar de hacer esto (el uso de la biblioteca estándar) es con la matemáticas.isclose función.

    Se tiene la siguiente firma:

    isclose(a, b, rel_tol=1e-9, abs_tol=0.0)

    Un ejemplo de uso con absoluta tolerancia a errores:

    from math import isclose
    a = 1.0
    b = 1.00000001
    assert isclose(a, b, abs_tol=1e-8)

    Si quieres que con la precisión de n dígitos significativos, simplemente reemplace la última línea con:

    assert isclose(a, b, abs_tol=10**-n)

    OriginalEl autor erb

  3. 8

    Aquí está una toma.

    def nearly_equal(a,b,sig_fig=5):
        return ( a==b or 
                 int(a*10**sig_fig) == int(b*10**sig_fig)
               )
    usted realmente no necesita que la instrucción if. usted puede simplemente devolver el resultado de la comparación: es Verdadero o Falso, es más corta y seguramente más python
    en este trabajo para la nearlyequal((1e9,1+1e9, 5)?
    buen punto @silentghost. Hizo el cambio.
    Esta falla nearlyequal(1e9, 1e9 + 1 , 5) – no funciona para números grandes.
    El problema es que las “cifras significativas” depende de si el número es >1 o <1. >1 tienes que utilizar sig_fig-matemáticas.log10(n) para cambiar “a la derecha”. Si <1 simple uso sig_fig a cambiar “a la izquierda”.

    OriginalEl autor Triptych

  4. 4

    Creo que tu pregunta no está definido bastante bien, y la unidad de pruebas que presente probarlo:

    Si por ‘ronda a N sig-fig decimales’ que significa ‘N decimales a la derecha del punto decimal, entonces el test de assert nearlyequal(1e9, 1e9 + 1 , 5) en caso de fallar, porque incluso cuando ronda 1000000000 y 1000000001 a 0.00001 exactitud, son diferentes.

    Y si por ‘ronda a N sig-fig decimales’ que significa ‘El N la mayoría de los dígitos significativos, independientemente del punto decimal, entonces el test de assert nearlyequal(-1e-9, 1e-9, 5) en caso de fallar, porque 0.000000001 y -0.000000001 son totalmente diferentes cuando se ve de esta manera.

    Si usted significó la primera definición, entonces la primera respuesta en esta página (Tríptico) es buena.
    Si nos referimos a la segunda definición, decir por favor, prometo pensar acerca de ella 🙂

    En realidad, estás en lo correcto acerca de “hacer valer nearlyequal(-1e-9, 1e-9, 5)” – rompe las reglas! +1

    OriginalEl autor Oren Shemesh

  5. 3

    Ya hay un montón de respuestas, pero aquí es un pensar:

    def closeness(a, b):
      """Returns measure of equality (for two floats), in unit
         of decimal significant figures."""
      if a == b:
        return float("infinity")
      difference = abs(a - b)
      avg = (a + b)/2
      return math.log10( avg / difference )
    
    
    if closeness(1000, 1000.1) > 3:
      print "Joy!"

    OriginalEl autor SingleNegationElimination

  6. 2

    “Cifras significativas” en decimal es una cuestión de ajustar el punto decimal y truncar a entero.

    >>> int(3.1415926 * 10**3)
    3141
    >>> int(1234567 * 10**-3)
    1234
    >>>
    Puede usted proporciona un algoritmo para hacer esto?
    Diferentes desde el código ya he publicado? ¿Qué más necesitas saber?

    OriginalEl autor S.Lott

  7. 1

    Este es un problema bastante común con números de punto flotante. Voy a resolver se basa en la discusión en la Sección 1.5 de Demmel[1]. (1) Calcular el error de redondeo. (2) Compruebe que el error de redondeo es menos de lo que algunos epsilon. No he utilizado python en tiempo y sólo tiene la versión 2.4.3, pero voy a tratar de conseguir este correcto.

    El paso 1. Error de redondeo

    def roundoff_error(exact, approximate):
        return abs(approximate/exact - 1.0)

    El paso 2. De punto flotante de igualdad

    def float_equal(float1, float2, epsilon=2.0e-9):
        return (roundoff_error(float1, float2) < epsilon)

    Hay un par de deficiencias obvias con este código.

    1. División por cero de error si el valor exacto es Cero.
    2. No comprueba que los argumentos son valores de punto flotante.

    Revisión 1.

    def roundoff_error(exact, approximate):
        if (exact == 0.0 or approximate == 0.0):
            return abs(exact + approximate)
        else:
            return abs(approximate/exact - 1.0)
    
    def float_equal(float1, float2, epsilon=2.0e-9):
        if not isinstance(float1,float):
            raise TypeError,"First argument is not a float."
        elif not isinstance(float2,float):
            raise TypeError,"Second argument is not a float."
        else:
            return (roundoff_error(float1, float2) < epsilon)

    Que un poco mejor. Si el exacto o aproximado valor es cero, que el error es igual al valor de la otra. Si algo además de un valor de punto flotante, un TypeError es elevada.

    En este punto, la única cosa difícil es establecer el valor correcto para epsilon. Me di cuenta de que en la documentación de la versión 2.6.1 de que hay un épsilon atributo en sys.float_info, así que yo uso dos veces ese valor como el valor predeterminado epsilon. Pero el valor correcto depende tanto de su aplicación y a su algoritmo.

    [1] James W. Demmel, Aplicada Álgebra Lineal Numérica, SIAM, 1997.

    OriginalEl autor tmh

  8. 1

    Oren Semes hay una parte de el problema con el problema como se indica pero hay más:

    afirmar nearlyequal( 0.0, 1e-15, 5 )

    también falla la segunda definición (y esa es la definición que he aprendido en la escuela).

    No importa cuántos dígitos que estás mirando, 0 no es igual a una no-cero. Esto podría llegar a ser un dolor de cabeza para este tipo de análisis si usted tiene un caso cuya respuesta correcta es cero.

    OriginalEl autor Loren Pechtel

  9. 1

    Hay una interesante solución para esto por B. Dawson (con código C++)
    en “La Comparación De Números De Punto Flotante”. Su enfoque se basa en el estricto IEEE representación de dos números y la imposición de la ordenación lexicográfica cuando dijo que se representan los números como enteros sin signo.

    OriginalEl autor Dan M.

  10. 0

    Hay un montón de maneras de comparar dos números para ver si están de acuerdo con N dígitos significativos. Aproximadamente hablando, usted sólo quiere asegurarse de que su diferencia es menor que 10^-N veces el mayor de los dos números que se comparan. Eso es bastante fácil.

    Pero, ¿qué pasa si uno de los números es cero? El concepto de relación-diferencias o significativos-dígitos se cae cuando se comparan contra cero. Para manejar ese caso, usted necesita tener una absoluta diferencia, la cual debe ser especificado de manera diferente a partir de la relación de diferencia.

    Me discutir los problemas de la comparación de los números de punto flotante — incluyendo un caso específico de la manipulación de cero, en este post del blog:

    http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/

    OriginalEl autor Bruce Dawson

  11. 0

    Me han pedido para probar una biblioteca proporcionado por una 3era parte

    Si usted está utilizando el valor predeterminado de Python unittest marco, puede utilizar assertAlmostEqual

    self.assertAlmostEqual(a, b, places=5)

    OriginalEl autor serv-inc

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *