FLOAT Problem
Andreas Korthaus
- php
Hallo!
Ich habe ein kleines Problem. Und zwar vergleiche ich in einem PHP Script einen berechneten Wert(FLOAT) mit einem $_POST Wert(STRING).
Berechneter Wert wird aus 2 Strings(da aus DB) berechnet, z.B.:
// $_POST['wert'] = '0.28'
$wert1 = '0.2900000000'; // STRING
$wert2 = '0.0100000000'; // STRING
$berechneter_wert = $wert1 - $wert2; // PHP konvertiert nach FLOAT
if($berechneter_wert < $_POST['wert']) {
var_dump($berechneter_wert); // ergibt: float(0.28)
var_dump($_POST['wert']); // ergibt: string(4) "0.28"
}
In diesem Fall erwartete ich eigentlich, dass die If-Abfrage scheitert, da 0.28 == 0.28 und nicht 0.28 < 0.28. Aber FLOAT scheint mir da einen Strich durch die Rechnung zu machen. Und im Augenblick habe ich keine Ahnung was ich dagegen machen soll. Ich denke das liegt an den vielen Nachkommastellen der Strings aus der DB, aber die brauche ich eigentlich, da ich manchmal 10 Nachkommastellen brauche.
Was könnte ich hier machen? Oder vielleicht die Nullen am Ende abschneiden? Wie am besten? Ich wüßte jetzt nur mit einem regulären Ausdruck, geht das auch ohne?
Viele Grüße
Andreas
Hi,
Was könnte ich hier machen?
ich würde vermeiden, Variablen unterschiedlichen Typs miteinander zu vergleichen, also beide auf die selbe Basis (in diesem Fall: Float) bringen. Wenn das immer noch Ungenauigkeiten bringt - die Problematik Dezimalsystem kontra Binärsystem kennen wir alle - dann versuche wenn möglich, auf n Nachkommastellen zu beschränken (int(x*10^n)).
Cheatah
Hi!
ich würde vermeiden, Variablen unterschiedlichen Typs miteinander zu vergleichen, also beide auf die selbe Basis (in diesem Fall: Float) bringen.
Aber das ist es ja was PHP automatisch macht, da bringt es wenig wenn ich das selbst vorher mache. Ich habe jetzt einfach die FLOAT-Zahl in einen String umgewandelt, und dann geht es.
Kann ich das so machen? Also das:
if((STRING) $berechneter_wert < $_POST['wert']) {
}
Auch wenn ich anstatt Dezimalzahlen INT-Zahlen in die DB schreibe und die Nachkommastellen nur in der Anzeige hinzufüge, da verlagere ich das Problem nur auf später, wenn ich dann durch 1000000000 teile, denn vorher habe ich ja bereits FLOAT-Zahlen wenbn ich 2 INT-Zahlen durcheinander Teile und das Ergebnis nicht aufgeht.
Besonders unangenehm ist hier, dass solche Fehler nur sporadisch auftreten, und schwer zu finden sind...
Viele Grüße
Andreas
Kann ich das so machen? Also das:
if((STRING) $berechneter_wert < $_POST['wert']) {
}
Nur was wenn die FLOAT-Zahl nicht sowas ist wie 0.28, sondern mehr Nachkommastellen hat als der POST-Wert?
Grüße
Andreas
Moin Moin !
Aus Numerik I:
Zwei Floats sind gleich, wenn der Absolutbetrag ihrer Differenz kleiner als eine sehr kleine, aber noch als Float darstellbaren Zahl Epsilon ist.
const epsilon = 1.0e-10;
function float_equal(float a, float b)
{
if (abs(a-b)<epsilon) {
return 'equal'
} else {
return 'not equal'
}
}
Alexander
Hi!
Aus Numerik I:
Oh je... genau das waren die Gründe warum ich doch nicht Informatik studiert habe... und jetzt braucht man da ja tatsächlich...
Zwei Floats sind gleich, wenn der Absolutbetrag ihrer Differenz kleiner als eine sehr kleine, aber noch als Float darstellbaren Zahl Epsilon ist.
Ok, aber im PHP-Manual steht dass, FLOAT normalerweise auf 14 Nachkommastellen genau rechnet. Ich habe 10, also was kann da schief gehen? Ist es ein Unterschied ob ich 0.2800000000 - 0.0100000000 rechne, oder 0.28 - 0.01? Wenn ja - wie komme ich zu letzterer Version?`Der Einfachste Weg wäre die Umwandlung in FLOAT, nur bleibt das Umrechnubgsproblem dann wohl bestehen, das andere wäre einfach die hinteren Nullen aus dem String zu entfernen - weiß jemand von Euch womit man das in PHP ohne RegExpr. machen kann?
const epsilon = 1.0e-10;
function float_equal(float a, float b)
{
if (abs(a-b)<epsilon) {
return 'equal'
} else {
return 'not equal'
}
}
Oder meinst Du ich soll den Vergleich mit eben dieser Funktion durchführen?
Viele Grüße
Andreas
Moin Moin !
Zwei Floats sind gleich, wenn der Absolutbetrag ihrer Differenz kleiner als eine sehr kleine, aber noch als Float darstellbaren Zahl Epsilon ist.
Ok, aber im PHP-Manual steht dass, FLOAT normalerweise auf 14 Nachkommastellen genau rechnet.
Ach ja?
123456789012345678901234567890123456789012345678901234567890.12345678901234
ist für PHP also noch von
123456789012345678901234567890123456789012345678901234567890.12345678901235
zu unterscheiden? Ich glaube nicht.
0.2800000000000000000
0.2800000000000000001
0.2799999999999999999
Alle drei Zahlen werden als 0.28 angezeigt.
Ich habe 10, also was kann da schief gehen?
Die Langfassung? => Numerik I.
Ist es ein Unterschied ob ich 0.2800000000 - 0.0100000000 rechne, oder 0.28 - 0.01?
Nein. Aber wenn der Computer rechnet, arbeitet er mit Exponent und Mantisse, und da geht schonmal was verloren. Dezimale Zahlen lassen sich generell nicht exakt in binäre Zahlen mit Exponent und Mantisse umrechnen. Ausnahmen bestätigen die Regel.
const epsilon = 1.0e-10;
function float_equal(float a, float b)
{
if (abs(a-b)<epsilon) {
return 'equal'
} else {
return 'not equal'
}
}Oder meinst Du ich soll den Vergleich mit eben dieser Funktion durchführen?
Mit einer sehr ähnlichen. Überlege, wann die Differenz zwischen zwei FLOATs für Deinen Anwendungsfall zu vernachlässigen ist -- oder nimm 1e-10.
Alexander
Hi,
Zwei Floats sind gleich, wenn der Absolutbetrag ihrer Differenz kleiner als eine sehr kleine, aber noch als Float darstellbaren Zahl Epsilon ist.
sei Epsilon größer Null so klein gewählt, dass Epsilon Halbe schon negativ ist.
Cheatah, SCNR ;-)
Moin Moin !
sei Epsilon größer Null so klein gewählt, dass Epsilon Halbe schon negativ ist.
Das erklär mal jemandem, der die Binärdarstellung von Floats nicht kennt. ;-)
Für manche Anwendungen ist Epsilon=1.0E-6 oder 1.0E-10 schon mehr als ausreichend.
Alexander