truncar redondear por numero los decimales debajo cortar como python math floating-point numpy

por - redondear un numero en python 3



Función para determinar si dos números son casi iguales cuando se redondean a n dígitos decimales significativos (10)

"Cifras significativas" en decimales es una cuestión de ajustar el punto decimal y truncar a un entero.

>>> int(3.1415926 * 10**3) 3141 >>> int(1234567 * 10**-3) 1234 >>>

Se me ha pedido que pruebe una biblioteca proporcionada por un tercero. Se sabe que la biblioteca es precisa para n cifras significativas. Cualquier error menos significativo se puede ignorar de forma segura. Quiero escribir una función para ayudarme a comparar los resultados:

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

El propósito de esta función es determinar si dos números de coma 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 redondean para firmar figuras significativas cuando se escriben en decimal.

¿Alguien puede sugerir una buena implementación? He escrito una mini unidad de prueba. A menos que pueda ver un error en mis pruebas, una buena implementación debería pasar lo siguiente:

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 a y b pueden ser de tipo int, float o numpy.float64. Los valores a y b siempre serán del mismo tipo. Es vital que la conversión no introduzca un error adicional en la función.
  2. Permite mantener este valor numérico, por lo que las funciones que se convierten en cadenas o utilizan trucos no matemáticos no son ideales. Este programa será auditado por alguien que sea un matemático que querrá poder demostrar que la función hace lo que se supone que debe hacer.
  3. Velocidad ... Tengo que comparar una gran cantidad de números así que cuanto más rápido mejor.
  4. Tengo numpy, scipy y la biblioteca estándar. Cualquier otra cosa será difícil para mí, especialmente para una parte tan pequeña del proyecto.

Aquí hay una toma.

def nearly_equal(a,b,sig_fig=5): return ( a==b or int(a*10**sig_fig) == int(b*10**sig_fig) )


Este es un problema bastante común con los números de coma flotante. Lo resuelvo en base a la discusión en la Sección 1.5 de Demmel [1]. (1) Calcule el error de redondeo. (2) Verifique que el error de redondeo sea menor que algunos epsilon. No he utilizado Python en algún momento y solo tengo la versión 2.4.3, pero intentaré corregir esto.

Paso 1. Error de redondeo

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

Paso 2. Igualación de punto flotante

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 error cero si el valor exacto es cero.
  2. No verifica que los argumentos sean valores de coma 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)

Eso es un poco mejor. Si el valor exacto o aproximado es cero, entonces el error es igual al valor del otro. Si se proporciona algo además de un valor de punto flotante, se genera un TypeError.

En este punto, lo único difícil es establecer el valor correcto para épsilon. Noté en la documentación para la versión 2.6.1 que hay un atributo epsilon en sys.float_info, así que usaría el doble de ese valor como épsilon predeterminado. Pero el valor correcto depende tanto de su aplicación como de su algoritmo.

[1] James W. Demmel, álgebra lineal numérica aplicada , SIAM, 1997.


Oren Shemesh tiene parte del problema con el problema tal como está establecido, pero hay más:

assert almostequal (0.0, 1e-15, 5)

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

No importa cuántos dígitos esté mirando, 0 no será igual a un valor distinto de cero. Esto podría ser un dolor de cabeza para tales pruebas si tiene un caso cuya respuesta correcta es cero.


Ya hay muchas respuestas excelentes, pero aquí hay una idea:

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!"


Hay una solución interesante para esto por B. Dawson (con código C ++) en "Comparación de números de coma flotante" . Su enfoque se basa en la representación IEEE estricta de dos números y el ordenamiento lexicográfico impuesto cuando dichos números se representan como enteros sin signo.


Creo que su pregunta no está lo suficientemente definida, y las pruebas unitarias que presenta lo demuestran:

Si por ''redondo a N lugares decimales decimales'' quieres decir ''N lugares decimales a la derecha del punto decimal'', entonces la prueba assert nearlyequal(1e9, 1e9 + 1 , 5) debería fallar, porque incluso cuando redondeas 1000000000 y 1000000001 a 0.00001 de precisión, todavía son diferentes.

Y si por ''ronda a N lugares decimales sigmiles'' te refieres a ''Los N dígitos más significativos, independientemente del punto decimal'', entonces la prueba assert nearlyequal(-1e-9, 1e-9, 5) debería fallar, porque 0.000000001 y -0.000000001 son totalmente diferentes cuando se ven de esta manera.

Si se refería a la primera definición, entonces la primera respuesta en esta página (por Tríptico) es buena. Si te refieres a la segunda definición, dilo, prometo pensar en ello :-)


Hay muchas maneras de comparar dos números para ver si aceptan N dígitos significativos. A grandes rasgos, solo quiere asegurarse de que su diferencia sea inferior a 10 ^ -N veces el mayor de los dos números que se comparan. Eso es bastante fácil.

Pero, ¿y si uno de los números es cero? El concepto completo de diferencias relativas o dígitos significativos cae cuando se compara con cero. Para manejar ese caso, también debe tener una diferencia absoluta, que debe especificarse de manera diferente a la diferencia relativa.

Discuto los problemas de comparar números de coma flotante, incluido un caso específico de manejo cero, en esta publicación de blog:

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


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.


A partir de Python 3.5, la forma estándar de hacer esto (usando la biblioteca estándar) es con la función math.isclose .

Tiene la siguiente firma:

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

Un ejemplo de uso con tolerancia absoluta al error:

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

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

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