2010-01-03 14 views
5
$onethird = 1.0/3; 
$fivethirds = 1.0/3+1.0/3+1.0/3+1.0/3+1.0/3; 
$half = 1.0/2; 
$threehalf = 1.0/2+1.0/2+1.0/2; 
var_dump($onethird + $fivethirds == $half + $threehalf); 

que da salida a false, pero como todos sabemos: 5/3+1/3=2=3/2+1/2¿Cómo solucionar este problema en PHP?

Cómo solucionar este problema?

Respuesta

5

Este es uno de los problemas con la representación IEEE 754 de números en coma flotante; las representaciones no son lo suficientemente precisas para representar todos los números racionales.

La manera de hacerlo es comparar la diferencia en contra de un número muy pequeño de cercanía, en lugar de la igualdad:

abs(($onethird + $fivethirds) - ($half + $threehalf)) < 1e-8 
+1

Esta es la naturaleza de los números de coma flotante en general, entonces, ¿qué tiene que ver con el estándar IEEE 754? –

+0

PHP está escrito en C y obtiene su soporte de FP a partir de eso, y la mayoría de los tiempos de ejecución C usan IEEE 754. Además, IEEE 854 usa una raíz variable y puede admitir más precisión para algunos números a costa de cierta velocidad de procesamiento cuando calculando con ellos. –

+0

Es la naturaleza de los números de coma flotante en general. El valor de épsilon (aquí 0.000000001) depende del número de bits que representan el float. – slebetman

4

El problema proviene de los pequeños errores introducidos por Floating Point arithmetic. Esto no es específico de PHP.

Puede solucionar esto introduciendo un pequeño factor de "tolerancia", es decir, verificando que el primer valor en la comparación es> = el segundo valor menos la tolerancia y < = el segundo valor más la tolerancia.

1
var_dump(abs($onethird + $fivethirds - $half + $threehalf) < 0.00001); 

véase: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm también: http://docs.sun.com/source/806-3568/ncg_goldberg.html

Esto es cierto en todos los lenguajes de programación.

Todavía no he encontrado ningún idioma en el que esto se haga automáticamente cuando los programadores quieren la igualdad de flotadores. Alguien debería llegar a un nuevo operador, tal vez = ~ = por la igualdad flotador que haría automáticamente la comparación de diff con épsilon:

if ($float1 =~= $float2) {... 

Es molesto que todos los años, desde que me gradué en el año 2000, alrededor de este tiempo del año, un novato hará esta pregunta en algún grupo de noticias, foro o lista de correo. El mes pasado respondí esto en comp.lang.tcl. Y no son solo principiantes, hace dos meses tuve que explicarle esto a mi compañero de trabajo que ha estado desarrollando software por más de 5 años preguntándome por qué su código Perl no funciona.

1

Cómo solucionar este problema?

¿Qué actual Problema ¿quieres resolverlo? Su código solo muestra que los números de coma flotante tienen una precisión limitada, eso no es nada nuevo.

Para la mayoría de las aplicaciones del mundo real, la entrada que no es 100% precisa de todos modos, y el resultado solo necesita ser preciso con un par de decimales. Las comparaciones de igualdad simplemente no son algo que necesites la mayor parte del tiempo. Si lo hace, puede cambiar el color al ver si el resultado está dentro de una pequeña distancia predefinida de un número determinado.

Si necesita matemáticas decimales con precisión específica, use las funciones BC Math, pero tenga en cuenta que son muy lentas si se utilizan para cálculos complejos.

Cuestiones relacionadas