BASHで「浮動数値」を丸める方法を検索するために数時間を費やしましたが、正しいものが見つかりません!回避策:(この数字をExcelに入力すると、小数点以下の2桁まで丸めた後に正しい結果が得られます。
3.314 -> 3.31
3.315 -> 3.32
8.124 -> 8.12
8.125 -> 8.13
BASHで正確な結果を得るには?printf
試してみましたが、awk
同じ結果が得られませんでした。
prompt> printf '%.*f\n' 2 8.125
8.12
prompt> echo '8.125' | awk '{printf("%.2f\n", $1)}'
8.12
答え1
printf '%.2f\n' 8.125
8.125
strtod()
10進数のテキスト表現は、標準またはC関数などのstrtold()
方法を使用して内部バイナリ表現に変換され、結果としてバイナリ浮動小数点数になり、内部表現はCコンパイラとdouble
コンピュータlong double
アーキテクチャによって異なります。
その後、数値はコマンドを介して標準関数(または結果を文字列として返すバリアント)printf
に渡され、それをリテラル10進表現に変換して丸めます。printf()
最も近い偶数に丸め、偶数に丸め(とも呼ばれる)銀行家の丸め)。
学校で学んだ方法は、平均的に(正の場合)過大評価する傾向があるという点で偏っています。半分が偶数の場合、8.125
合計8.115
は8.12
(2
でも)、1ビットは丸められ、1ビットは丸められる。大きな数字の場合、数字の半分を四捨五入し、残りの半分を四捨五入しても体系的な偏りは発生しません。
これは、2進数で正確に表現できる数値にのみ適用されます。
偶然にも、8.125は2進数で表すことができます。それは2 3 + 2 -3です。 8.135 最も近い IEEE 754 倍精度 2 進数は 8.134999999999999997868371792719699442386627197265625(10 進数に再変換するとき) なので偶数 (8.4 偶数 (8.1)
したがって、必要なものを実行するには(適切な銀行丸めも実行する)、2進数では機能しませんが、10進数では機能する必要があります。したがって、ユーティリティをprintf()
含む、以下で使用されるか、またはより一般的にバイナリを処理するプロセッサの浮動小数点算術関数と連携することは不可能です。printf
awk
printf()
numfmt
python3
たとえば、次decimal
のモジュールを使用できます。@marcelmが表示)perl
またはMath::BigFloat
基準寸法そしてそれはbfround()
機能です。基本的に彼らはそうです銀行家の丸め:
$ printf '%s\n' 1.1{0..5}5 | perl -MMath::BigFloat -lne 'print "$_ -> " . Math::BigFloat->new($_)->bfround(-2)'
1.105 -> 1.10
1.115 -> 1.12
1.125 -> 1.12
1.135 -> 1.14
1.145 -> 1.14
1.155 -> 1.16
ただし、次のように切り替えることができます。common
モデルこれは学校で学んだ内容と一致します。
$ printf '%s\n' {-,}1.1{0..5}5 +825e-3 0xAAp-6 0x1p100 nan Infinity |
perl -MMath::BigFloat -lne '
print "$_ -> " . Math::BigFloat->new($_)->bfround(-2,common)'
-1.105 -> -1.11
-1.115 -> -1.12
-1.125 -> -1.13
-1.135 -> -1.14
-1.145 -> -1.15
-1.155 -> -1.16
1.105 -> 1.11
1.115 -> 1.12
1.125 -> 1.13
1.135 -> 1.14
1.145 -> 1.15
1.155 -> 1.16
+825e-3 -> 0.83
0xAAp-6 -> 2.66
0x1p100 -> 1267650600228229401496703205376.00
nan -> NaN
Infinity -> inf
(他の記号を使用する追加の数字が表示されます)。
likepython3
のメソッドは、numfmt
一部のprintf
ユーティリティ実装とは異なり、ロケールを区別しません。特に、10進数の基数文字は、ロケール.
でも入出力に使用されます。,
٫
答え2
システムがGNU coreutilsを使用している場合数字コマンドを使用できる必要があり、「上」、「下」、「0から」(デフォルト)、「0」、または「最も近い」丸めから選択できます。
たとえば、(ここでは、小数点のデフォルト文字がロケールから.
):
$ numfmt --round=nearest --format %.2f << EOF
3.314
3.315
8.124
8.125
EOF
3.31
3.32
8.12
8.13
答え3
整数に四捨五入する標準的な方法は、½を加えて切り取ることです。
printf '%s\n' 1.5 2.5 3.5 4.5 |
awk '{ print int($1 +.5) }'
小数点以下の2桁に100を掛けるには、切り捨てて再分割します。
printf '%s\n' 3.314 3.315 8.124 8.125 |
awk '{ print int(100*$1 +.5)/100 }'
または
printf '%s\n' 3.314 3.315 8.124 8.125 |
awk '{ printf "%0.2f\n", int(100*$1 +.5)/100 }'
答え4
Pythonの使用
python3 -c 'import decimal, sys; print(round(decimal.Decimal(sys.argv[1]), ndigits=2))' 3.314
Pythonは、Decimal
他のほとんどの回答で発生する浮動小数点精度の問題なしに、デフォルトの10進数を無制限の精度で正確に表します。
存在するdecimal
基本コンテキストPythonはround()
半分の値を偶数に丸めます(バンカー丸めと呼ばれます)。これは、たわみを導入しないので、一般的に好ましい丸め方法である。
出力例:
3.314 -> 3.31
3.315 -> 3.32
8.124 -> 8.12
8.125 -> 8.12
10000.195 -> 10000.20
必要な動作を得るためにゼロから四捨五入することもできます。
$ python3 -c 'import sys; from decimal import *; getcontext().rounding = ROUND_HALF_UP; print(round(Decimal(sys.argv[1]),2))' 8.125
8.13
これは8.125を8.13に丸め、-8.125を-8.13に丸めます。
getcontect().prec
10 26以上(または-10 26以下)に丸めることができる数値を処理する必要がある場合は、値を増やします(デフォルトは28)。
バラよりこのdecimal
文書もっと学ぶ。
1は次の式で決定されます。コンテキストの //prec
Emin
Emax
非常に大きい(無限ではありませんが)最大値を持ちます。