2012-05-04 21 views
5

Me estoy enfrentando a un error ¿realmente extraño? en mysql + php en este momento. es una selecta simple, en el siguiente ejemplo estoy usando múltiples campos a tratar de explicar mi problema:MySQL round error extraño

  • "campo" es de 11,5
  • $ 1.15 phpvar es
  • consulta

MySQL:

select round(field * " . $phpvar . " ,2) as a1, 
     round(field * 1.15 ,2) as a2, 
     round(11.5 * " . $phpvar . " ,2) as a3, 
     round(11.5 * 1.15 ,2) as a4, 
     field * " . $phpvar . " as a5 
from ... 

bien, estoy tratando de obtener 13.23. "field" * $phpvar = 13.225, entonces usando ronda (13.225,2) debería obtener 13.23, ¿verdad? bueno, sí y no

resultados de la consulta:

  • A1 [redonda (". $ Phpvar" campo *, 2)] => 13.22
  • a2 [redonda (campo * 1.15, 2)] => 13.22
  • a3 [redonda (11.5 * "$ phpvar.", 2)] => 13.23
  • a4 [redonda (11.5 * 1.15, 2)] => 13.23
  • a5 [campo * ". $ phpvar. "] => 13.225 (sin ronda)

¿Qué me estoy perdiendo? ¿Cómo es posible, cuando se trata de usar "campo", mi resultado obtiene una ronda falsa?

+1

¿Cómo se define el campo en la base de datos? – Anigel

+0

seleccione el campo * 1.15 y 11.5 * 1.15 sin redondear, como a6 y a7 y publique el resultado. – jishi

+0

¿Simplemente usa 'ceil()' en su lugar? – Nick

Respuesta

6

El problema es cómo se almacenan los valores DOUBLE y FLOAT.

Es posible (y probable) que valores como 11.5 o 22.475 puedan almacenarse en valores aproximados como 11.499999999999 ~ o 22.475000000000000001, por lo que algunos cálculos o redondeos podrían dar lugar a resultados incorrectos.

Siempre es mejor almacenar valores flotantes en DECIMAL coulmn tipo donde el valor se almacena exactamente con todos los dígitos decimales y no es aproximado.

+0

Gracias, usar DECIMAL funciona bien, lo tendré en cuenta para el futuro – ipronet

+0

@ipronet ¡De nada! – shadyyx

+0

@ipronet También marque la respuesta si fue útil. ¡Gracias! – shadyyx

5

literales numéricos exactos (como el 1.15 en todos los cuatro de sus ejemplos y la 11.5 en su dos últimos) son encoded utilizando MySQL de tipo DECIMAL.

En sus dos primeros ejemplos, el resultado de multiplicar field (de tipo DOUBLE) con tales literales es otra DOUBLE, que se almacena utilizando 8-byteIEEE Standard representación de coma flotante (es decir binary64). Sin embargo, en esta representación 13.225 codifica como 0x402A733333333333, cuyos bits significar:

 
Sign   : 0b0 

Biased exponent: 0b10000000010 
       = 1026 (representation includes bias of +1023, therefore exp = 3) 

Significand : 0b[1.]1010011100110011001100110011001100110011001100110011 
       = [1.]6531249999999999555910790149937383830547332763671875 
        ^hidden bit, not stored in binary representation 

Esto equivale a:

 
    (-1)^0 * 1.6531249999999999555910790149937383830547332763671875 * 2^3 
=   13.2249999999999996447286321199499070644378662109375000 

Por lo tanto, el redondeo de este resultado con dos decimales produce 13.22 no 13.23.

Realizando la multiplicación con dos tipos de DECIMAL, como dos literales como los usados ​​en los últimos dos ejemplos, da como resultado otro DECIMAL.En esta representación, 13.225 codifica en un formato binary-coded decimal con precisión exacta y, por lo tanto, la operación de redondeo da como resultado 13.23 como se esperaba.

como se menciona en la MySQL manual:

números de coma flotante a veces causan confusión porque son aproximados y no se almacenan como valores exactos. Un valor de coma flotante tal como está escrito en una declaración SQL puede no ser el mismo que el valor representado internamente. Los intentos de tratar los valores de punto flotante como exactos en las comparaciones pueden generar problemas. También están sujetos a dependencias de plataforma o implementación. Los tipos de datos FLOAT y DOUBLE están sujetos a estos problemas. Para las columnas DECIMAL, MySQL realiza operaciones con una precisión de 65 dígitos decimales, lo que debería resolver los problemas de inexactitud más comunes.

Si busca la precisión exacta, DECIMAL puede ser más adecuado a sus necesidades. Si necesita el rango/rendimiento de DOUBLE pero aún desea obtener el resultado de redondeo deseado, puede CONVERT el resultado de la multiplicación a DECIMAL antes del redondeo.