如何在命令行上正确舍入 IEEE 754 浮点数?
我想指定输出数字的精度 - 小数位数。
例如,四舍五入6.66
到精度1
应给出。6.7
更多内容见下表:
Value Precision Rounded
6.66 0 7
6.66 1 6.7
6.66 2 6.66
6.66 3 6.660
6.666 3 6.666
6.6666 3 6.667
它应该可以在交互式 shell 中使用,但理想情况下足够强大,可以在生产 shell 脚本中使用它。
答案1
对浮点数进行四舍五入
“对浮点数进行四舍五入”是什么意思?
显然,这很简单......我学校的数学书在哪里......
不,我们已经知道与浮点数相关的事情并不容易:
首先,有多种舍入模式:
向上舍入?
向下舍入?
四舍五入到零?
四舍五入到最接近的值 - 是否为偶数?
四舍五入到最接近的值 - 远离零?
如何处理极端情况?如何找出哪些是极端情况?
好的,看来我们最好使用 IEEE 754 标准的实现,并让我们的系统来处理这个问题。
要在 shell 中对浮点数进行舍入,基于标准浮点运算,我们需要三个步骤:
- 将输入文本从命令行参数转换为标准浮点数。
- 使用正常的 IEEE 754 实现对浮点数进行舍入。
- 将数字格式化为字符串以进行输出。
原来是shell命令printf
可以做到这一切。它可用于根据格式规范打印数字,如中所述man 3 printf
。如果输出格式需要,数字将以标准方式隐式舍入:
命令
使用输入作为命令行参数舍入x
到数字精度:p
printf "%.*f\n" "$p" "$x"
或者在 shell 管道中,使用x
标准输入作为输入并p
作为参数:
echo "$x" | xargs printf "%.*f\n" "$p"
例子:
$ 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
坏陷阱
当心地域!.
正如您所期望的,它指定整数部分和分数部分之间的分隔符 - 。
但看看你自己在德国语言环境中会发生什么,例如:
$ LC_ALL=de_DE.UTF-8 printf '%.*f\n' 3 6.6666
6,667
是的,没错6,667
- 六个逗号六六七。这肯定会弄乱你的脚本。
(但仅限于德国的两个客户。目前正在为这些客户调试的开发者机器除外。)
更坚固
为了使其更加健壮,请使用:
LC_ALL=C /usr/bin/printf "%.*f\n" "$p" "$x"
或者
echo "$x" | LC_ALL=C xargs /usr/bin/printf "%.*f\n" "$p"
这也使用/usr/bin/printf
而不是 shell 内置的bash
orzsh
来解决printf
变体实现中的微小不一致,并防止在德语语言环境中LC_ALL
设置但未导出时出现非常脏的效果。然后,内置使用,
,并/usr/bin/printf
使用.
...
另请参阅%g
舍入到指定有效数字位数。
答案2
我发现的最简单的干净阅读方式是:
$ print "%.2f" $(bc <<< "0.10765432*100)
10.77
现在,5
改为4
10.76
它易于阅读且简短,易于维护。
答案3
以下对我有用。
#!/bin/bash
function float() {
bc << EOF
num = $1;
base = num / 1;
if (((num - base) * 10) > 1 )
base += 1;
print base;
EOF
echo ""
}
float 3.2