Bash 中的浮点数运算
发布于 2008年7月30日
仔细想想,令人惊讶的是,有很多编程任务不需要使用浮点数。 如果您是嵌入式系统程序员,那么在 C 程序中使用“double”可能会被解雇。 如果您编写 PHP 或 JavaScript,请快速回答,它们甚至支持浮点数吗? Bash 就是一种不支持浮点数的语言,但让我们不要因此而停下脚步...
为 bash 添加浮点功能的明显选择是bc. bc引用手册页
任意精度计算器语言顺便说一句,请注意引文中的最后一个词:“语言”。 是的bc实际上是一种编程语言,它包含if语句和while循环等。 我说是顺便说一句,因为它与我们今天要做的事情在很大程度上无关,但并非完全无关。
为了在我们的 bash 脚本中使用bc,我们将把它打包成几个函数
float_eval EXPRESSION and float_cond CONDITIONAL-EXPRESSION这两个函数都期望一个浮点表达式,float_eval将表达式的评估结果写入标准输出,float_cond假设该表达式是一个条件表达式,如果表达式为真,则将返回/状态代码设置为零,如果为假,则设置为一。
用法非常简单
float_eval '12.0 / 3.0' if float_cond '10.0 > 9.0'; then echo 'As expected, 10.0 is greater than 9.0' fi a=12.0 b=3.0 c=$(float_eval "$a / $b")
函数的代码如下
#!/bin/bash
#
# Floating point number functions.
#####################################################################
# Default scale used by float functions.
float_scale=2
#####################################################################
# Evaluate a floating point number expression.
function float_eval()
{
local stat=0
local result=0.0
if [[ $# -gt 0 ]]; then
result=$(echo "scale=$float_scale; $*" | bc -q 2>/dev/null)
stat=$?
if [[ $stat -eq 0 && -z "$result" ]]; then stat=1; fi
fi
echo $result
return $stat
}
#####################################################################
# Evaluate a floating point number conditional expression.
function float_cond()
{
local cond=0
if [[ $# -gt 0 ]]; then
cond=$(echo "$*" | bc -q 2>/dev/null)
if [[ -z "$cond" ]]; then cond=0; fi
if [[ "$cond" != 0 && "$cond" != 1 ]]; then cond=0; fi
fi
local stat=$((cond == 0))
return $stat
}
# Test code if invoked directly.
if [[ $(basename $0 .sh) == 'float' ]]; then
# Use command line arguments if there are any.
if [[ $# -gt 0 ]]; then
echo $(float_eval $*)
else
# Turn off pathname expansion so * doesn't get expanded
set -f
e="12.5 / 3.2"
echo $e is $(float_eval "$e")
e="100.4 / 4.2 + 3.2 * 6.5"
echo $e is $(float_eval "$e")
if float_cond '10.0 > 9.3'; then
echo "10.0 is greater than 9.3"
fi
if float_cond '10.0 < 9.3'; then
echo "Oops"
else
echo "10.0 is not less than 9.3"
fi
a=12.0
b=3.0
c=$(float_eval "$a / $b")
echo "$a / $b" is $c
set +f
fi
fi
# vim: tabstop=4: shiftwidth=4: noexpandtab:
# kate: tab-width 4; indent-width 4; replace-tabs false;
函数的工作是通过将参数提供给bc:
result=$(echo "scale=$float_scale; $*" | bc -q 2>/dev/null)默认情况下bc输出的结果小数点右边没有数字,也没有小数点。 要更改此设置,您必须更改bc的内置变量之一scale。 这就是bc的“语言”功能发挥作用的地方,在bc中,就像在 C 中一样,语句用分号分隔。 我们设置bc的scale变量,方法是在传递给bc的表达式前加上scale=$float_scale;。 这将bc中的 scale 设置为 bash 全局变量float_scale的值,默认设置为 2(脚本顶部附近)。
这里的主要陷阱与“*”、“<”和“>”在 bash 中具有其他含义有关。 您可以通过引用表达式来消除“<”和“>”的问题,但这仅适用于使用单引号的“*”,这意味着您不能在表达式中包含 bash 变量。 另一种选择是用 "set -f" 和 "set +f" 包围您的代码,以关闭路径名/通配符扩展。
如果您将脚本保存为float.sh并直接运行它,它将执行底部的测试代码
$ sh float.sh 12.5 / 3.2 is 3.90 100.4 / 4.2 + 3.2 * 6.5 is 44.70 10.0 is greater than 9.3 10.0 is not less than 9.3 12.0 / 3.0 is 4.00
您可能提出的一个未解答的问题是:“我为什么要这样做?” 下次我将向您展示一个您可以将其用于实际用途的地方。