2011-01-12 16 views
10

Wenn eine Float-Nummer auf eine bestimmte Zahl nach dem Gleitkomma abgeschnitten werden muss, stellt sich heraus, dass es nicht einfach ist, getan werden. Zum Beispiel, wenn das Abschneiden der zweiten Ziffer nach dem Punkt erfolgen soll, sollten die Zahlen
sein. 45,8976 => 45,89, 0,0185 => 0,01
(zweite Ziffer nach dem Punkt wird nicht entsprechend der dritten Ziffer nach dem Punkt gerundet).
Funktionen wie round(), number_format(), sprintf() um die Anzahl und ausdrucken
45,8976 => 45,90, 0,0185 => 0,02
Trimmen von Zahlen mit PHP

Ich habe zwei Lösungen erfüllt, und ich frage mich, ob sie gut sind genug und das ist besser zu

1. function truncNumber($number, $prec = 2) 
{ 
     return bccomp($number, 0, 10) == 0 ? $number : round($number - pow(0.1, bcadd( $prec, 1)) * 5, $prec); 
} 

2. function truncNumber($number, $prec = 2) 
{ 
    return sprintf("%.".$prec."f", floor($number*pow(10, $prec))/pow(10, $prec)); 
} 
+0

Hallo. Woher hast du diese Funktion? haben sie richtig für dich gearbeitet? welchen hast du gewählt und warum? – arod

Antwort

16

floor verwendet werden tun, was Sie fragen.

floor(45.8976 * 100)/100; 

Sie werden eine direktere Funktion dafür nicht finden, da es eine seltsame Sache ist, zu fragen. Normalerweise werden Sie mathematisch korrigieren. Aus Neugier - Wofür brauchst du das?

+2

Ja, das ist wahrscheinlich die beste Lösung, um positive Float-Zahlen zu kürzen. Um mit negativen Gleitkommazahlen zu arbeiten, sollte abs() der Zahl genommen werden und das Zeichen danach hinzugefügt werden. Es ist eine Formel, die aus dem Geschäft kommt, dass, wenn es eine zu lange Fließkommazahl erzeugt, alle Ziffern nach der zweiten ignoriert werden sollten von abgerundet :). –

+2

Wenn wir nach dem Fließkomma bereits zwei Ziffern haben, aber nichts davon wussten und es mit Ihrem Code versuchen: 'echo floor (10.04 * 100)/100;' gibt '10.03' zurück. – jamapag

+1

Wie bei Jampag. Echo Boden (4.60 * 100)/100; wird 4.59 zurückgeben, was falsch ist. – Jimmy

1

Die round()-Funktion hat einen Genauigkeitsparameter sowie einen dritten Parameter zum Angeben der Rundungsmethode, aber Sie haben Recht, es schneidet nicht ab.

Was Sie suchen, sind die floor() und ceil() Funktionen. Der Nachteil ist, dass sie keine Präzision Parameter haben, so dass Sie etwas zu tun haben:

$truncated = floor($value*100)/100; 
1

ich eine andere Methode zu sehen, diese auszuführen:

function trunc($float, $prec = 2) { 
    return substr(round($float, $prec+1), 0, -1); 
} 

Aber es ist nicht besser als alle anderen ... round kann auch durch sprintf ersetzt werden.

6

Zahlen trunkieren die „beste“ ist (ich hier nahm eine Zahl, die für mein Beispiel funktioniert) zu verwenden:

$number = 120,321314; 

$truncate_number = number_format($number , 1); // 120,3 
$truncate_number = number_format($number , 2); // 120,32 
$truncate_number = number_format($number , 3); // 120,321 

Hoffnung diese Hilfe hier einfach als andere Antworten, aber es ist nur leicht die Fälle, für die es funktioniert. Hier ist ein Fall, es nicht funktioniert (demo):

$number = 10.046; 

    echo number_format($number , 2); // 10.05 

Die number_format Funktion heikel ist, können Sie Ihr Problem auf diese Weise lösen (von php.net):

Um die Runden zu verhindern, die auftritt, wenn nächste Ziffer nach dem letzten dezimal 5 (von mehreren Personen unten erwähnt):

<?php 

function fnumber_format($number, $decimals='', $sep1='', $sep2='') { 

     if (($number * pow(10 , $decimals + 1) % 10) == 5) 
      $number -= pow(10 , -($decimals+1)); 

     return number_format($number, $decimals, $sep1, $sep2); 
} 

$t=7.15; 
echo $t . " | " . number_format($t, 1, '.', ',') . " | " . fnumber_format($t, 1, '.', ',') . "\n\n"; 
//result is: 7.15 | 7.2 | 7.1 

$t=7.3215; 
echo $t . " | " . number_format($t, 3, '.', ',') . " | " . fnumber_format($t, 3, '.', ',') . "\n\n"; 
//result is: 7.3215 | 7.322 | 7.321 
} ?> 
+3

Wenn $ Nummer 10.046 ist, wird Ihr Code 10.05, nicht 10.04 zurückgeben. – jamapag

+0

Sie sind richtig Jungs und vermisste dieses Verhalten, aber ich denke ehrlich gesagt, das Verhalten ist seltsam, ich meine "number_format" der Name dieser Funktion machen Sie denken, ist wie Formatierung der Anzeige nur das Komma bewegen. – sandino

12

dies ist meine Lösung:

/** 
* @example truncate(-1.49999, 2); // returns -1.49 
* @example truncate(.49999, 3); // returns 0.499 
* @param float $val 
* @param int f 
* @return float 
*/ 
function truncate($val, $f="0") 
{ 
    if(($p = strpos($val, '.')) !== false) { 
     $val = floatval(substr($val, 0, $p + 1 + $f)); 
    } 
    return $val; 
} 
+0

warum 'abs()', sollte es nicht benötigt werden. Recht? – hakre

+0

Es ist richtig. Ich kann mich wirklich nicht erinnern, warum ich in dieser Funktion 0 ( –

+0

) abs() hinzufügen musste, also etwas, von dem wir nicht wissen, ob es notwendig ist oder nicht, kann nicht "das Einfachste" an meinen Augen sein. nur sagen, dass Sie wahrscheinlich das ein wenig umschreiben und die nicht notwendigen Teile aus Ihrer Antwort entfernen sollten, um besseres Feedback zu bekommen) – hakre

2

Obwohl diese Frage alt ist, werde ich eine andere Antwort posten, für den Fall, dass jemand wie ich über dieses Problem stolpert.Eine einfache Lösung für positive Zahlen ist:

function truncate($x, $digits) { 
    return round($x - 5 * pow(10, -($digits + 1)), $digits); 
} 

ich es gegen eine String-basierte Lösung mit 10 Millionen zufälligen Fraktionen getestet und sie immer abgestimmt. Es zeigt nicht das Genauigkeitsproblem, das floor -basierte Lösungen tun.

Erweitern für Null und Negative ist ziemlich einfach.

1

Um dies zu tun, genau für beide + ve und -ve Zahlen, die Sie verwenden müssen:
- die PHP floor() Funktion für + ve Zahlen
- die PHP ceil() Funktion für -ve Zahlen

function truncate_float($number, $decimals) { 
    $power = pow(10, $decimals); 
    if($number > 0){ 
     return floor($number * $power)/$power; 
    } else { 
     return ceil($number * $power)/$power; 
    } 
} 

der Grund dafür ist, dass floor() immer die Zahl rundet unten, nicht gegen Null.
dh floor() rundet effektiv -ve Zahlen in Richtung einer größeren Absolutwert
zB floor(1.5) = 1 während floor(-1.5) = 2

daher für die truncate-Methode multiply by power, round, then divide by power:
- floor() funktioniert nur für positive Zahlen
- ceil() funktioniert nur für negative Zahlen

Um dies zu testen, kopieren Sie den folgenden Code in den Editor von http://phpfiddle.org/lite (oder ähnlich):

<div>Php Truncate Function</div> 
<br> 
<?php 
    function truncate_float($number, $places) { 
     $power = pow(10, $places); 
     if($number > 0){ 
      return floor($number * $power)/$power; 
     } else { 
      return ceil($number * $power)/$power; 
     } 
    } 

    // demo 
    $lat = 52.4884; 
    $lng = -1.88651; 
    $lat_tr = truncate_float($lat, 3); 
    $lng_tr = truncate_float($lng, 3); 
    echo 'lat = ' . $lat . '<br>'; 
    echo 'lat truncated = ' . $lat_tr . '<br>'; 
    echo 'lat = ' . $lng . '<br>'; 
    echo 'lat truncated = ' . $lng_tr . '<br><br>'; 

    // demo of floor() on negatives 
    echo 'floor (1.5) = ' . floor(1.5) . '<br>'; 
    echo 'floor (-1.5) = ' . floor(-1.5) . '<br>'; 
?> 
8
function truncate($value) 
{ 
    if ($value < 0) 
    { 
     return ceil((string)($value * 100))/100; 
    } 
    else 
    { 
     return floor((string)($value * 100))/100; 
    } 
} 

Warum behandeln PHP nicht mathamatic Formeln, um die Art, wie wir erwarten würden, z.B. floor(10.04 * 100) = 1003 wenn wir alle wissen, sollte es 1004 sein. Dieses Problem tritt nicht nur in PHP auf, sondern kann in allen Sprachen auftreten, abhängig vom relativen Fehler in der verwendeten Sprache. PHP verwendet das Format IEEE 754 mit doppelter Genauigkeit, das einen relativen Fehler von etwa 1,11e-16 aufweist. (resource)

Das eigentliche Problem ist, dass die floor-Funktion den Float-Wert in einen int-Wert, z. (int)(10.04 * 100) = 1003 wie wir in der Bodenfunktion früher sehen. (resource)

Um dieses Problem zu lösen, können wir den Float zu einer Zeichenkette umwandeln, eine Zeichenkette kann einen beliebigen Wert genau darstellen, dann wird die Zeichenkette die Zeichenkette genau zu einem int umwandeln.

0

Ich möchte meinen Beitrag zu dieser Antwort mit der Funktion hinzufügen, die ich für meine Bedürfnisse verwende, die abgeschnittene Positionen sowohl für negative als auch für positive Werte berücksichtigt.

function truncate($val,$p = 0) 
{ 
    $strpos = strpos($val, '.'); 
    return $p < 0 ? round($val,$p) : ($strpos ? (float)substr($val,0,$strpos+1+$p) : $val); 
} 

... Ich denke, es ist einfach und schnell zu bedienen.

0

Der Grund dafür ist die interne binäre Darstellung von Fließkommazahlen. Der angegebene Wert von oben 10.04 muss intern als Potenz der Basis 2 dargestellt werden.

Der mögliche Exponent für den Gleitkommateil ist ln(0,04)/ln(2) = -4,64.... Dies zeigt bereits, dass 10.04 nicht exakt als Binärzahl dargestellt werden kann. Wir haben am Ende etwas wie 10.04 ~ 2^3 + 2^1 + 2^-5 + 2^-7 + 2^-11 + ... mit einer maximalen Präzision für den Gleitkomma-Teil.

Dies ist der Grund, 10.04 ist intern tatsächlich ein bisschen weniger und könnte als 10.039... dargestellt werden.

Um dieses Verhalten zu umgehen, gibt es zwei Möglichkeiten - entweder direkt mit String-Operationen herumspielen oder eine beliebige Präzisions-Bibliothek wie BcMath für PHP verwenden.

<?php 
function test($value) 
{ 
    // direct string operations 
    $fromString = (float)preg_replace(
     '#(?:(\.\d{1,2})\d*)?$#', 
     '${1}', 
     (string)$value 
    ); 
    // using arbitrary precision 
    // @see http://php.net/manual/en/function.bcadd.php 
    $fromBcMath = (float)bcadd(
     (string)$value, 
     '0', 
     2 
    ); 

    var_dump($fromString, $fromBcMath); 
} 

test(3); 
test(4.60); 
test(10.04); 
test(45.8976); 

der Ausgang für den obigen Code generiert (I hinzugefügt newlines zur besseren Lesbarkeit Trenn):

double(3) 
double(3) 

double(4.6) 
double(4.6) 

double(10.04) 
double(10.04) 

double(45.89) 
double(45.89)