7 Stimmen

Wie führe ich eine Fließkommarundung mit einer Vorspannung durch (immer auf- oder abrunden)?

Ich möchte die Schwimmer mit einer Vorspannung abrunden, entweder immer nach unten oder immer nach oben. Es gibt einen bestimmten Punkt im Code, an dem ich dies benötige, der Rest des Programms sollte wie üblich auf den nächsten Wert runden.

Ich möchte zum Beispiel auf das nächste Vielfache von 1/10 runden. Die nächstgelegene Fließkommazahl zu 7/10 ist ungefähr 0,69999998807, aber die nächstgelegene Zahl zu 8/10 ist ungefähr 0,80000001192. Wenn ich Zahlen abrunden möchte, erhalte ich diese beiden Ergebnisse. Mir wäre es lieber, wenn sie auf die gleiche Weise gerundet würden. 7/10 sollte auf 0,70000004768 gerundet werden und 8/10 sollte auf 0,80000001192 gerundet werden.

In diesem Beispiel runde ich immer auf, aber ich habe einige Stellen, an denen ich immer abrunden möchte. Glücklicherweise habe ich es an jeder dieser Stellen nur mit positiven Werten zu tun.

Die Zeile, die ich zum Runden verwende, lautet floor(val * 100 + 0.5) / 100 . Ich programmiere in C++.

11voto

Pieter Punkte 16973

Ich denke, der beste Weg, dies zu erreichen, ist, sich auf die Tatsache zu stützen, dass nach dem IEEE 754 Fließkommastandard die Ganzzahldarstellung von Fließkommabits lexikografisch als 2-Komplement-Ganzzahl geordnet ist.

D.h. Sie könnten einfach einen ulp (Einheiten an letzter Stelle) hinzufügen, um die nächste Fließkommadarstellung zu erhalten (die immer etwas größer sein wird als Ihr Schwellenwert, wenn er kleiner war, da der Rundungsfehler höchstens 1/2 ulp beträgt)

z.B..

 float floatValue = 7.f/10;
 std::cout << std::setprecision(20) << floatValue << std::endl;
 int asInt = *(int*)&floatValue;
 asInt += 1;
 floatValue = *(float*)&asInt;
 std::cout << floatValue << std::endl;

druckt (auf meinem System)

 0.69999998807907104492
 0.70000004768371582031

Um herauszufinden, wann Sie ein ulp hinzufügen müssen, müssen Sie sich auf die Differenz von floor und eine abgerundete floor

 if (std::floor(floatValue * 100.) != std::floor(floatValue * 100. + 0.5)) {
    int asInt = *(int*)&floatValue;
    asInt += 1;
    floatValue = *(float*)&asInt;
 }

Würde 0,69 korrekt in 0,70 umwandeln, aber 0,80 in Ruhe lassen.

Beachten Sie, dass der Float durch die Multiplikation mit 100. vor dem floor angewendet wird.

Wenn Sie das nicht tun, riskieren Sie, in eine Situation zu geraten, die für

 7.f/10.f * 100.f

Die (in der Genauigkeit begrenzte) Float-Darstellung wäre 70,00...

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X