如何像 MS Excel 一样在 bash 中四舍五入到小数点后两位?

use*_*454 8 bash numeric-data excel

我花了几个小时搜索如何在 BASH 中对“浮动数字”进行舍入,但找不到任何正确的!解决方案:( 如果我将这些数字放入 Excel,那么我将在四舍五入到小数点后两位后收到正确的结果:

3.314 -> 3.31
3.315 -> 3.32
8.124 -> 8.12
8.125 -> 8.13
Run Code Online (Sandbox Code Playgroud)

如何在BASH中得到准确的结果?我尝试使用printfandawk但没有得到相同的结果

prompt> printf '%.*f\n' 2 8.125
8.12
prompt> echo '8.125' | awk '{printf("%.2f\n", $1)}'
8.12
Run Code Online (Sandbox Code Playgroud)

Sté*_*las 16

\n
printf \'%.2f\\n\' 8.125\n
Run Code Online (Sandbox Code Playgroud)\n
\n

将使用标准或C 函数8.125之类的方法将十进制数的文本表示形式转换为内部二进制表示形式,获得一个或二进制浮点数,其内部表示形式将取决于 C 编译器和计算机体系结构。strtod()strtold()doublelong double

\n

然后该数字将通过命令传递printfprintf()标准函数(或其将结果返回为字符串的变体),该函数会将其转换回文本十进制表示形式并进行舍入。这种四舍五入到最接近的四舍五入,一半到偶数(也称为银行家四舍五入)。

\n

您在学校学到的方法存在偏差,因为它往往会平均高估(对于正数)。一半为偶数时, 和 均8.125四舍五入8.115(偶数8.122,一位向下舍入,一位向上舍入。对于大量数字,将其中一半数字向下舍入,另一半向上舍入,就不会引入系统偏差。

\n

现在,这只适用于可以用二进制精确表示的数字。

\n

碰巧,8.125 可以用二进制表示。那是 2 3 + 2 -3。8.135 不能。最接近的 IEEE 754 双精度二进制数是 8.1349999999999997868371792719699442386627197265625(转换回十进制时),因此它将转换为 8.13,因为它最接近,而不是偶数 (8.14)。

\n

因此,要做你想做的事(甚至进行适当的银行四舍五入),你需要一些不能以二进制工作但以十进制工作的东西。因此,任何在下面使用的东西,printf()包括printf实用程序awk\'s printf(),或者更一般地与处理二进制数的处理器浮点算术功能一起使用,numfmt都是不可能的。

\n

例如,您可以使用python3\'s decimalmodule (如@marcelm 所示)或perl\'s Math::BigFloatmodule and its bfround()function。默认情况下,他们进行银行四舍五入

\n
$ printf \'%s\\n\' 1.1{0..5}5 | perl -MMath::BigFloat -lne \'print "$_ -> " . Math::BigFloat->new($_)->bfround(-2)\'\n1.105 -> 1.10\n1.115 -> 1.12\n1.125 -> 1.12\n1.135 -> 1.14\n1.145 -> 1.14\n1.155 -> 1.16\n
Run Code Online (Sandbox Code Playgroud)\n

但你可以切换到与你在学校学到的模式相匹配的common 模式

\n
$ printf \'%s\\n\' {-,}1.1{0..5}5 +825e-3 0xAAp-6 0x1p100 nan Infinity | \n   perl -MMath::BigFloat -lne \'\n     print "$_ -> " . Math::BigFloat->new($_)->bfround(-2,common)\'\n-1.105 -> -1.11\n-1.115 -> -1.12\n-1.125 -> -1.13\n-1.135 -> -1.14\n-1.145 -> -1.15\n-1.155 -> -1.16\n1.105 -> 1.11\n1.115 -> 1.12\n1.125 -> 1.13\n1.135 -> 1.14\n1.145 -> 1.15\n1.155 -> 1.16\n+825e-3 -> 0.83\n0xAAp-6 -> 2.66\n0x1p100 -> 1267650600228229401496703205376.00\nnan -> NaN\nInfinity -> inf\n
Run Code Online (Sandbox Code Playgroud)\n

(显示使用不同符号表示的额外数字)。

\n

请注意,类似于python3\ 的方法,但numfmt与某些printf实用程序实现不同,它不区分区域设置。特别是,.即使在否则为 或 的语言环境中,,小数基数字符也将用于输入和输出\xd9\xab

\n


ste*_*ver 12

如果您的系统使用 GNU coreutils,则numfmt命令应该可用,并允许您在舍入 \xe2\x80\x98up\xe2\x80\x99、\xe2\x80\x98down\xe2\x80\x99、\xe2\ 之间进行选择x80\x98from-zero\xe2\x80\x99(默认值)、\xe2\x80\x98towards-zero\xe2\x80\x99 或 \xe2\x80\x98nearest\xe2\x80\x99。

\n

例如(此处在十进制基数字符为 的区域设置中.):

\n
$ numfmt --round=nearest --format %.2f << EOF\n3.314\n3.315\n8.124\n8.125\nEOF\n3.31\n3.32\n8.12\n8.13\n
Run Code Online (Sandbox Code Playgroud)\n

  • 但随后看到`echo 10000.185 | numfmt --round=nearest --format %.2f`(在 GNU/Linux amd64 计算机上) (2认同)