2012-02-06 11 views
5

Questo problema si esprime al meglio nel codice:Problema con moltiplicazione galleggiante e valutazione

$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) uscite: float(25000) e quando il cast a int, uscite: int(24999) - e si trova in tal modo il problema.

Non so davvero cosa fare a riguardo. Il problema si verifica in seguito alla moltiplicazione per 100, e mentre ci sono piccoli trucchi che posso fare per ovviare a questo (come ad esempio * 10 * 10) mi piacerebbe sapere se esiste una soluzione "reale" a questo problema.

Grazie :)

+0

Letture consigliate: http://floating-point-gui.de/ –

risposta

0

Questo è ciò che galleggia fare a volte, è tutto a causa di come galleggianti sono in grado di rappresentare con precisione interi di volta in volta.

Invece di trasmetterlo a un int, è possibile arrotondare il numero a un valore intero e quindi trasmetterlo a un int. (probabilmente questo cast non è necessario, ma PHP non chiarisce come accadono tali cose internamente, e anche se sai come stanno accadendo in questo momento, potrebbero non farlo in futuro.

0

Penso che potresti usare bccomp per confrontare valori in virgola mobile, ma penso che sia una funzione che non è nel PHP core.

Altrimenti Ho trovato questa funzione here ma non ho potuto provare a vedere se funziona

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

si tratta di un orribile hacky soluzione e mi odio leggermente per questo, ma questo dà il comportamento previsto:

<?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!'; 
    } 

Trasferirli in stringhe e vengono convertiti in int/float in base al confronto. Non mi piace ma funziona.

In realtà è necessario il BC Math per la matematica in virgola mobile in PHP.

0

Il problema è che i float non possono rappresentare solo alcuni numeri. Dal momento che PHP non ha un tipo "decimale" (o altro punto fisso), puoi fondamentalmente aggirare solo questi problemi.

Supponendo che il primo numero nel tuo esempio $var1 = 286.46 denota qualche genere di soldi, si può solo convertire in centesimi direttamente dopo che l'utente entrato (ad esempio attraverso nudo il punto e la lettura come un intero) e, quindi, calcolare tutto utilizzando integer matematica.

Questa non è una soluzione generale - e dubito che ne esista uno (a parte l'uso di numeri di precisione arbitrari, forniti da alcune estensioni PHP - ma io mi sento un po 'eccessivo).

1

È sempre consigliabile utilizzare ceil (o floor in base a ciò che si desidera) quando si utilizza il numero float come int Nel tuo caso prova ceil ($ var4) prima del confronto!