2009-06-23 7 views
0

Ich habe eine schreckliche Zeit, mich selbst zu überzeugen, was ich hier getan habe, ist eine gute Idee. Der spezifische Abschnitt I zu beanstanden ist:Ungenaue Zahlen mit Mikrozeit und Fließkomma-Addition in PHP

return ((float)($now+$sec).'.'.$mic); 

Um die Gleitkommagenauigkeit zu erhalten, bin ich gezwungen, entweder wieder auf dem BC oder GMP-Bibliotheken fällt (von denen keiner ist immer verfügbar). In diesem Fall habe ich versucht, die Zahlen zusammen mit der String-Verkettung einzufangen.

<?php 

// return the current time, with microseconds 
function tick() { 
    list($sec, $mic, $now) = sscanf(microtime(), "%d.%d %d"); 
    return ((float)($now+$sec).'.'.$mic); 
} 

// compare the two given times and return the difference 
function elapsed($start, $end) { 
    $diff = $end-$start; 

    // the difference was negligible 
    if($diff < 0.0001) 
     return 0.0; 

    return $diff; 
} 

// get our start time 
$start = tick(); 

// sleep for 2 seconds (should be ever slightly more than '2' when measured) 
sleep(2); 

// get our end time 
$end = tick(); 

$elapsed = elapsed($start, $end); 

// should produce output similar to: float(2.00113797188) 
var_dump($elapsed); 

?> 

Wenn ich Versuch, zwei Zahlen zu addieren, wie (das einen Zeitstempel) und 0,0987654321 (repräsentiereMikroSekunden), die Zugabe Operator ( +) I unveränderlich mit 123456789,099 enden. Auch wenn die Ganzzahl in float umgewandelt wird, ist das Ergebnis dasselbe.

Gibt es eine Lösung für dieses Problem, die 1) kein Hack ist und 2) keine String-Verkettung beinhaltet? Ich sollte nicht auf diese Art von verworrenem Code zurückgreifen müssen, um einen genauen Zeitstempel mit Mikrosekundenauflösung zu erhalten.

Bearbeiten: Wie S. Gehrig erklärt hat, können Fließkommazahlen in PHP manchmal etwas knifflig dargestellt werden. Die in der PHP-Konfiguration angegebene "Genauigkeit" bezieht sich auf die Anzeige. Die tatsächlichen Werte sind nicht abgerundet, wie ich dachte. Eine weitaus einfachere Lösung für den obigen Code würde wie so aussehen:

// return the current time, with microseconds 
function tick() { 
    return microtime(true); 
} 

// compare the two given times and return the difference 
function elapsed($start, $end) { 
    return $end-$start; 
} 

// get our start time 
$start = tick(); 

// sleep for 2 seconds (should be ever slightly more than '2' when measured) 
sleep(2); 

// get our end time 
$end = tick(); 

$elapsed = elapsed($start, $end); 

// should produce output similar to: float(2.00113797188) 
var_dump($elapsed); 

Wenn Sie $ zu untersuchen sind beginnen oder $ beenden, bevor eine von dem anderen subtrahiert wird, könnte es scheint sie auf die Hundertstel gerundet wurden Position. Das ist nicht der Fall. Es scheint, dass willkürliche Genauigkeit für Arithmetik beibehalten wird, während die Anzeige begrenzt ist.

Antwort

2

Warum verwenden Sie nicht microtime(true), die einfach einen Mikrosekunden-Zeitstempel als float zurückgibt? Der Parameter [bool] $get_as_float wurde in PHP 5.0.0 hinzugefügt.

Zu der Bemerkung über den "Verlust" der Präzision:

$start = microtime(true); 
$end = microtime(true); 
echo $end - $start; 
// prints 7.1526861190796 

microtime(true) wird auf 2 Dezimalstellen beschränkt. Auf das Poster trifft der Einfluss der Konfigurationseinstellung precision, die steuert, wie viele Dezimalstellen bei der Ausgabe von Float-Variablen gedruckt werden. Das hat nichts mit der internen Präzision microtime(true) zu tun. Sie können immer number_format() oder (s)printf() verwenden, um die Ausgabe auf die von Ihnen gewünschte Genauigkeit zu formatieren.

+0

würde ich, aber die Präzision ist auf 2 Stellen gerundet und es scheint nicht eine Möglichkeit, dies zu ändern. Standardmäßig beträgt die Genauigkeit von PHP 12 Stellen ... diese Funktion wird von dieser Funktion jedoch nicht berücksichtigt. Überprüfen Sie es mit: php -r 'echo microtime (wahr);' Ich habe keine Ahnung, was bewirkt, dass es so rund wird. – spligak

+0

Die Genauigkeit von 12 Dezimalstellen für Float enthält die Dezimalstellen vor dem Komma - also ist die Präzision von 12 Stellen genau das, was Sie sehen. –

+0

Dies ist nur ein Anzeigeproblem und wird von der Konfigurationseinstellung 'precision' gesteuert: http://us2.php.net/manual/en/ini.core.php#ini.precision. Intern hat ein Float eine Genauigkeit von etwa 14 Dezimalziffern. –

0

Gleitkommatypen sind von Natur aus ungenau. Entweder lebe damit, oder benutze sie nicht.

+0

Ultimatum notiert. Ich nehme an, ich werde damit leben. – spligak

+0

BC, GMP oder Strings stellen die Alternative dar "verwende sie nicht". –

+0

Sie können Floating-Point verwenden, aber entscheiden Sie sich nicht mit so viel Ungenauigkeit zu leben. http://en.wikipedia.org/wiki/Kahan_summation_algorithm –

1

Erstens, Spligak, sehe ich, dass Ihr Code einen Fehler enthält.

list($sec, $mic, $now) = sscanf(microtime(), "%d.%d %d"); 
return ((float)($now+$sec).'.'.$mic); 

Wenn $ mic weniger als sechs Ziffern hat, erhalten Sie Müll-Ergebnisse.Haben einen Schreibtisch Überprüfung

the case where microtime() returns "0.000009 1234567890" 

Zweitens können Sie erheblich die Floating-Point-Fehler reduzieren, wie folgt: (ACHTUNG: nicht getesteten Code)

// die beiden angegebenen Zeiten vergleichen und gibt die Differenz

// get our start time 
$start = microtime(); 

// sleep for 2 seconds (should be ever slightly more than '2' when measured) 
sleep(2); 

// get our end time 
$end = microtime(); 

// work around limited precision math 
// subtract whole numbers from whole numbers and fractions from fractions 

list($start_usec, $start_sec) = explode(" ", $start); 
list($end_usec, $end_sec) = explode(" ", $end); 
$elapsed = ((float)$end_usec)-((float)$start_usec); 
$elapsed += ((float)$end_sec)-((float)$start_sec); 

// please check the output 
var_dump($elapsed);