Rundung von Gleitkommazahlen
Was bedeutet "Runden einer Gleitkommazahl"?
Das ist natürlich einfach ... Wo ist mein Mathematikbuch von der Schule ...
Nein, wir wissen bereits, dass nichts in Bezug auf Gleitkommazahlen einfach ist:
Zunächst gibt es mehrere Rundungsmodi:
Aufrunden?
Abrunden?
Auf Null runden?
Rundung auf die nächste - Bindung an gerade?
Rundung auf den nächsten Wert - von Null weg?
Wie gehe ich mit den Eckkoffern um? Wie finde ich heraus, welche Eckfälle es gibt?
OK, es sieht so aus, als würden wir besser eine Implementierung des IEEE 754-Standards verwenden und unser System sich darum kümmern lassen.
Um eine Gleitkommazahl in der Shell auf der Grundlage der Standard-Gleitkomma-Arithmetik zu runden, benötigen wir drei Schritte:
- Konvertieren Sie den Eingabetext von einem Befehlszeilenargument in eine Standard-Gleitkommazahl.
- Runden Sie die Gleitkommazahl mit der normalen IEEE 754-Implementierung.
- Formatieren Sie die Zahl als Zeichenfolge für die Ausgabe.
Es stellt sich heraus, dass der Shell-Befehl printfall dies kann. Es kann verwendet werden, um Zahlen gemäß einer Formatspezifikation wie in beschrieben zu drucken man 3 printf. Die Zahlen werden standardmäßig implizit gerundet, wenn dies für das Ausgabeformat erforderlich ist:
Der Befehl
Mit Genauigkeit als Befehlszeilenargumente xauf die pGenauigkeit der Ziffern runden :
printf "%.*f\n" "$p" "$x"
Oder in einer Shell-Pipeline mit Eingabe von xauf Standardeingabe und pals Argument:
echo "$x" | xargs printf "%.*f\n" "$p"
Beispiele:
$ printf '%.*f\n' 0 6.66
7
$ printf '%.*f\n' 1 6.66
6.7
$ printf '%.*f\n' 2 6.66
6.66
$ printf '%.*f\n' 3 6.66
6.660
$ printf '%.*f\n' 3 6.666
6.666
$ printf '%.*f\n' 3 6.6666
6.667
Schlechte Fallen
Vorsicht vor dem Gebietsschema! Es gibt das Trennzeichen zwischen dem Integral- und dem Bruchteil an - das ., wie Sie vielleicht erwarten.
Aber sehen Sie selbst, was in einem deutschen Gebietsschema passiert, zum Beispiel:
$ LC_ALL=de_DE.UTF-8 printf '%.*f\n' 3 6.6666
6,667
Ja, das stimmt 6,667- sechs Komma sechs sechs sieben. Das würde Ihr Skript sicher durcheinander bringen.
(Aber nur für die beiden Kunden in Deutschland. Mit Ausnahme der Entwicklermaschinen, die derzeit für diese Kunden debuggen.)
Robuster
Verwenden Sie:
LC_ALL=C /usr/bin/printf "%.*f\n" "$p" "$x"
oder
echo "$x" | LC_ALL=C xargs /usr/bin/printf "%.*f\n" "$p"
Dies verwendet /usr/bin/printfanstelle der in bashoder zshImplementierung kleinerer Inkonsistenzen bei der Implementierung der printfVarianten auch die Shell und umgeht einen sehr schmutzigen Effekt, wenn in einem deutschen Gebietsschema LC_ALLfestgelegt, aber nicht exportiert wird. Dann verwendet das eingebaute ,und /usr/bin/printfverwendet ....
Siehe auch %gzum Runden auf eine bestimmte Anzahl von signifikanten Stellen.
awk.