Ich habe einige Antworten auf dieser Seite gelesen und fand die printf
Rundung wünschenswert.
Als ich es jedoch in der Praxis verwendete, führte mich ein subtiler Fehler zu folgendem Verhalten:
$ echo 197.5 | xargs printf '%.0f'
198
$ echo 196.5 | xargs printf '%.0f'
196
$ echo 195.5 | xargs printf '%.0f'
196
Beachten Sie, dass Rundung 196.5
wird 196
.
Ich weiß, dass dies ein subtiler Gleitkomma-Fehler sein kann (aber das ist keine sehr große Zahl, oder?), Kann also jemand etwas Licht darauf werfen?
Eine Problemumgehung hierfür ist ebenfalls sehr zu begrüßen (da ich versuche, dies jetzt zum Laufen zu bringen).
bash
shell-script
printf
floating-point
math
Hai Zhang
quelle
quelle
Antworten:
Es ist wie erwartet, es ist "Round to Even" oder "Banker's Rounding".
Eine verwandte Site-Antwort erklärt es.
Das Problem, das eine solche Regel zu lösen versucht, ist das (für Zahlen mit einer Dezimalstelle),
Das sind 4 runter und 4 rauf.
Um die Rundung im Gleichgewicht zu halten, müssen wir die x.5 runden
Dies geschieht nach der Regel: «Auf die nächste gerade Zahl runden».
In Code:
Sch
LC_NUMERIC=C printf '%.0f ' "$value"
awk
echo "$value" | awk 'printf( "%s", $1)'
Optionen:
Insgesamt gibt es vier Möglichkeiten, eine Zahl zu runden:
Oben
Wenn Sie "Aufrunden (in Richtung
+infinite
)" benötigen , können Sie awk verwenden:awk
echo "$value" | awk '{ printf("%d", $1 + 0.5) }'
bc
echo "scale=0; ($value+0.5)/1" | bc
Nieder
Wenn Sie "Abrunden (Richtung
-infinite
)" benötigen , können Sie Folgendes verwenden:awk
echo "$value" | awk '{ printf("%d", $1 - 0.5) }'
bc
echo "scale=0; ($value-0.5)/1" | bc
Dezimalstellen kürzen.
Entfernen der Dezimalstellen (alles nach dem Punkt).
Wir könnten die Shell auch direkt verwenden (funktioniert bei den meisten Shells - ist POSIX):
Schale
echo "${value%%.*}"
awk
echo "$value"| awk '{printf ("%d",$0)}'
bc
echo "scale=0; ($value)/1" | bc
quelle
Es ist kein Fehler, es ist beabsichtigt.
Es macht eine Art Runde zum nächsten (dazu später mehr).
Mit genau können
.5
wir so oder so runden. In der Schule wurde dir wahrscheinlich gesagt, du sollst abrunden, aber warum? Weil Sie dann keine weiteren Ziffern mehr untersuchen müssen, zB 3.51 auf 4 aufrunden; 3.5 könnte ätherisch sein, aber wenn wir nur die erste Ziffer betrachten und .5 aufrunden, dann machen wir es immer richtig.Wenn wir uns jedoch den Satz von zweistelligen Dezimalstellen ansehen: 0,00 0,01, 0,02, 0,03… 0,98, 0,99, werden wir sehen, dass es 100 Werte gibt, 1 eine ganze Zahl ist, 49 aufgerundet werden müssen, 49 abgerundet werden müssen 1 (0,50) könnte ätherisch gehen. Wenn wir immer aufrunden, erhalten wir im Durchschnitt Zahlen, die 0,01 zu groß sind.
Wenn wir den Bereich auf 0 → 9,99 erweitern, haben wir 9 zusätzliche Werte für diese Aufrundung. Dadurch ist unser Durchschnitt etwas größer als erwartet. Ein Versuch, dies zu beheben, ist also: 0,5 Runden in Richtung gerade. Die Hälfte der Zeit, die es aufrundet, die Hälfte der Zeit, die es abrundet.
Dies ändert die Vorspannung von aufwärts zu gerade. In den meisten Fällen ist dies besser.
quelle
Das vorübergehende Ändern der Rundungsmodi ist nicht ungewöhnlich und es ist möglich,
bin/printf
dass Sie die Quellen ändern müssen, obwohl dies per se nicht der Fall ist.Sie benötigen die Quellen der Coreutils. Ich habe die neueste Version verwendet, die heute verfügbar ist: http://ftp.gnu.org/gnu/coreutils/coreutils-8.24.tar.xz .
Entpacken Sie mit in ein Verzeichnis Ihrer Wahl
tar xJfv coreutils-8.24.tar.xz
Wechseln Sie in das Quellverzeichnis
cd coreutils-8.24
Laden Sie die Datei
src/printf.c
in den Editor Ihrer Wahl und tauschen Sie die gesamtemain
Funktion mit der folgenden Funktion aus, einschließlich der beiden Präprozessoranweisungen, um die Headerdateienmath.h
und einzuschließenfenv.h
. Die Hauptfunktion befindet sich am Ende und beginntint main...
und endet am Ende der Datei mit der schließenden Klammer}
Führen Sie
./configure
wie folgt ausLIBS=-lm ./configure --program-suffix=-own
Das Suffix wird
-own
bei jedem Unterprogramm eingefügt (es gibt viele), nur für den Fall, dass Sie alle installieren möchten und nicht sicher sind, ob sie zum Rest des Systems passen. Die Coreutils werden nicht ohne Grund als Core Utils bezeichnet!Aber das Wichtigste ist das
LIBS=-lm
vor der Linie. Wir brauchen die mathematische Bibliothek und dieser Befehl weist Sie./configure
an, sie der Liste der benötigten Bibliotheken hinzuzufügen.Führen Sie make aus
make
Wenn Sie ein Multicore- / Multiprozessorsystem haben, versuchen Sie es
make -j4
Dabei sollte die Zahl (hier "4") die Anzahl der Kerne darstellen, die Sie für diesen Job bereit haben.
Wenn alles gut gegangen ist, haben Sie den neuen
printf
Intsrc/printf
. Versuch es:BIN_PRINTF_ROUNDING_MODE=1 ./src/printf '%.0f\n' 196.5
BIN_PRINTF_ROUNDING_MODE=2 ./src/printf '%.0f\n' 196.5
Beide Befehle sollten sich in der Ausgabe unterscheiden. Die Zahlen nach
IN_PRINTF_ROUNDING_MODE
bedeuten:Sie können das Ganze installieren (nicht empfohlen) oder einfach die Datei (vorheriges Umbenennen wird dringend empfohlen!)
src/printf
In ein Verzeichnis in Ihrem kopierenPATH
und wie oben beschrieben verwenden.quelle
Sie können den folgenden kurzen Einzeiler ausführen, wenn Sie tatsächlich für x.1 bis x.4 abrunden und für x.5 bis x.9 aufrunden möchten.
Oder ändern Sie "5" in das, was Sie wollen, z. B. "6".
PS bezüglich des Problems mit "." und / oder "," als Dezimaltrenner verwendet, ist hier eine einfache universelle Lösung.
quelle