2012-02-06 13 views
5

Este problema se expresa mejor en código:Problema con la multiplicación de flotación y evaluación

$var1 = 286.46; // user input data 
$var2 = 3646; // user input data 
$var3 = 25000; // minumum amount allowed 
$var4 = ($var1 * 100) - $var2; // = 250000 
if ($var4 < $var3) { // if 250000 < 250000 
    print 'This returns!'; 
} 

var_dump($var4) salidas: float(25000) y cuando se convierte a int, salidas: int(24999) - y con ello radica el problema.

No sé realmente qué hacer al respecto. El problema ocurre al multiplicar por 100, y si bien hay pequeños trucos que puedo hacer para eludir eso (como * 10 * 10) Me gustaría saber si hay una solución "real" para este problema.

Gracias :)

+0

Lectura recomendada: http://floating-point-gui.de/ –

Respuesta

0

Eso es lo que flota hacer a veces, todo se debe a la forma en flotadores son incapaces de representar con precisión números enteros de vez en cuando.

En lugar de convertirlo a un int, puede redondear el número a un valor entero y luego convertirlo en un int. (posiblemente ese molde innecesario, pero PHP no es claro sobre cómo suceden estas cosas internamente, e incluso si usted sabe cómo suceden en este momento, pueden no en el futuro.

0

Creo que podría usar bccomp para comparar valores de punto flotante, pero yo creo que es una función que no está en el PHP Core.

de lo contrario me encontré con esta función here pero no pude probarlo para ver si funciona

function Comp($Num1,$Num2,$Scale=null) { 
    // check if they're valid positive numbers, extract the whole numbers and decimals 
    if(!preg_match("/^\+?(\d+)(\.\d+)?$/",$Num1,$Tmp1)|| 
    !preg_match("/^\+?(\d+)(\.\d+)?$/",$Num2,$Tmp2)) return('0'); 

    // remove leading zeroes from whole numbers 
    $Num1=ltrim($Tmp1[1],'0'); 
    $Num2=ltrim($Tmp2[1],'0'); 

    // first, we can just check the lengths of the numbers, this can help save processing time 
    // if $Num1 is longer than $Num2, return 1.. vice versa with the next step. 
    if(strlen($Num1)>strlen($Num2)) return(1); 
    else { 
    if(strlen($Num1)<strlen($Num2)) return(-1); 

    // if the two numbers are of equal length, we check digit-by-digit 
    else { 

     // remove ending zeroes from decimals and remove point 
     $Dec1=isset($Tmp1[2])?rtrim(substr($Tmp1[2],1),'0'):''; 
     $Dec2=isset($Tmp2[2])?rtrim(substr($Tmp2[2],1),'0'):''; 

     // if the user defined $Scale, then make sure we use that only 
     if($Scale!=null) { 
     $Dec1=substr($Dec1,0,$Scale); 
     $Dec2=substr($Dec2,0,$Scale); 
     } 

     // calculate the longest length of decimals 
     $DLen=max(strlen($Dec1),strlen($Dec2)); 

     // append the padded decimals onto the end of the whole numbers 
     $Num1.=str_pad($Dec1,$DLen,'0'); 
     $Num2.=str_pad($Dec2,$DLen,'0'); 

     // check digit-by-digit, if they have a difference, return 1 or -1 (greater/lower than) 
     for($i=0;$i<strlen($Num1);$i++) { 
     if((int)$Num1{$i}>(int)$Num2{$i}) return(1); 
     else 
      if((int)$Num1{$i}<(int)$Num2{$i}) return(-1); 
     } 

     // if the two numbers have no difference (they're the same).. return 0 
     return(0); 
    } 
    } 
} 
1

ésta es una terrible hacky solución y me odio un poco por ello, pero esto da el comportamiento esperado:

<?php 

    $var1 = 286.46; // user input data 
    $var2 = 3646; // user input data 
    $var3 = 25000; // minumum amount allowed 
    $var4 = ($var1 * 100) - $var2; // = 250000 
    if ((string) $var4 < (string) $var3) { // if 250000 < 250000 
     print 'This returns!'; 
    } 

Colóquelos en cadenas, y se convierten de nuevo a int/flotante según corresponda para la comparación. No me gusta, pero funciona.

Realmente necesita BC Math para matemáticas con coma flotante precisa en PHP.

0

El problema es que las carrozas simplemente no pueden representar algunos números. Como PHP no tiene un tipo "decimal" (u otro tipo de punto fijo), básicamente solo puedes resolver estos problemas.

Suponiendo que el primer número en su ejemplo $var1 = 286.46 denota algún tipo de dinero, puede convertirlo a centavos directamente después de que el usuario lo ingresó (por ejemplo, eliminando el punto y leyéndolo como un entero) y así calcular todo usando enteros mates.

No es una solución general, y dudo que exista (a excepción del uso de números de precisión arbitrarios, que proporcionan algunas extensiones de PHP), pero a mí eso me huele demasiado caro.

1

Siempre es una buena idea usar ceil (o floor según lo que desee) cuando use el número de flotación como int En su caso pruebe ceil ($ var4) antes de comparar!

Cuestiones relacionadas