这是在 bash 中进行浮点到整数转换的正确方法吗?还有其他方法吗?
flotToint() {
printf "%.0f\n" "$@"
}
答案1
巴什
在 中bash
,这可能已经是最好的了。它使用 shell 内置函数。如果您需要变量中的结果,则可以使用命令替换或bash
特定命令(尽管现在 也支持zsh
):
printf -v int %.0f "$float"
你可以这样做:
float=1.23
int=${float%.*}
$float
但这会删除小数部分,而不是给您最接近的整数,并且这对于like1.2e9
或.12
example的值不起作用。
另请注意由于浮点数的内部表示而可能存在的限制:
$ printf '%.0f\n' 1e50
100000000000000007629769841091887003294964970946560
您确实得到了一个整数,但很可能您无法在任何地方使用该整数。
此外,正如 @BinaryZebra 所指出的,在几种printf
实现中(bash、ksh93、yash,而不是 zsh、dash 或旧版本的 GNU printf
),它受到区域设置(小数点分隔符可能是.
,,
或٫
)的影响。
因此,如果您的浮点数始终使用句点作为小数分隔符来表示,并且您希望无论调用脚本的用户的区域设置如何,都将其视为这样printf
,则需要将区域设置修复为 C:
LC_ALL=C printf '%.0f' "$float"
使用yash
,您还可以执行以下操作:
printf '%.0f' "$(($float))"
(见下文)。
POSIX
printf "%.0f\n" 1.1
不是 POSIX,因为%f
POSIX 不需要支持。
POSIXly,你可以这样做:
f2i() {
LC_ALL=C awk -- '
BEGIN {
for (i = 1; i < ARGC; i++)
printf "%.0f\n", ARGV[i]
}' "$@"
}
请注意,虽然awk
语言语法中的文字数字始终使用.
as 十进制基数字符(,
用于分隔参数,因此不能在数字中使用),但在像此处这样从输入中获取数字时ARGV
,某些实现会遵循区域设置的十进制基数特点。对于 GNU 来说,只有在环境中awk
时才是这样。$POSIXLY_CORRECT
桀骜
在zsh
(支持浮点算术(小数分隔符始终是句点))中,您可以使用rint()
数学函数为您提供最接近的整数作为浮点(如在 中C
),并int()
从浮点中为您提供整数(如在 中awk
)。所以你可以这样做:
$ zmodload zsh/mathfunc
$ i=$(( int(rint(1.234e2)) ))
$ echo $i
123
或者:
$ integer i=$(( rint(5.678e2) ))
$ echo $i
568
但请注意,虽然double
s 可以表示非常大的数字,但整数的限制要大得多。
$ printf '%.0f\n' 1e123
999999999999999977709969731404129670057984297594921577392083322662491290889839886077866558841507631684757522070951350501376
$ echo $((int(1e123)))
-9223372036854775808
克什93
ksh93 是第一个支持浮点运算的类 Bourne shell。当命令只是内置命令时,ksh93 通过不使用管道或分叉来优化命令替换。所以
i=$(printf '%.0f' "$f")
不分叉。或者甚至更好:
i=${ printf '%.0f' "$f"; }
它既不会分叉,也不会费尽心思创建一个假的子 shell 环境。
您还可以这样做:
i=$(( rint(f) ))
但要注意:
$ echo "$(( rint(1e18) ))"
1000000000000000000
$ echo "$(( rint(1e19) ))"
1e+19
你还可以这样做:
integer i=$(( rint(f) ))
但就像zsh
:
$ integer i=1e18
$ echo "$i"
1000000000000000000
$ integer i=1e19
$ echo "$i"
-9223372036854775808
请注意,ksh93
浮点算术遵循区域设置中的小数分隔符设置(即使,
是数学运算符($((1,2))
在法语/德语...区域设置中为 6/5,与 相同$((1, 2))
,在英语区域设置中为 2) 。
亚什
yash 还支持浮点运算,但没有ksh93
/zsh
等数学函数rint()
。您可以使用以下方法将数字转换为整数二进制或例如运算符(也适用于zsh
但不适用于ksh93
)。但请注意,它会截断小数部分,但不会给出最接近的整数:
$ echo "$(( 0.237e2 | 0 ))"
23
$ echo "$(( 1e19 | 0 ))"
-9223372036854775808
yash
在输出中尊重语言环境的小数分隔符,但不尊重其算术表达式中的浮点文字常量,这可能会导致意外:
$ LC_ALL=fr_FR.UTF-8 ./yash -c 'a=$((1e-2)); echo $(($a + 1))'
./yash: arithmetic: `,' is not a valid number or operator
它在某种程度上很好,因为您可以在使用句点的脚本中使用浮点常量,而不必担心它会在其他语言环境中停止工作,但仍然能够处理用户表示的数字,只要正如您记得要做的那样:
var=$((10.3)) # and not var=10.3
... "$((a + 0.1))" # and not "$(($a + 0.1))".
printf '%.0f\n' "$((10.3))" # and not printf '%.0f\n' 10.3
答案2
bc
- 任意精度计算器语言
int(float) 应该看起来像:
$ echo "$float/1" | bc
1234
为了更好地舍入,请使用以下命令:
$ echo "($float+0.5)/1" | bc
例子:
$ float=1.49
$ echo "($float+0.5)/1" | bc
1
$ float=1.50
$ echo "($float+0.5)/1" | bc
2
答案3
一个非常简单的 hacky 是
function float_to_int() {
echo $1 | cut -d. -f1 # or use -d, if decimals separator is ,
}
样本输出
$ float_to_int 32.333
32
$ float_to_int 32
32
答案4
有关的:
互补@斯特凡·查泽拉斯 awk
回答:
float_to_integer_rounding_nearst() {
awk 'BEGIN{for (i=1; i<ARGC;i++) printf "%.0f", ARGV[i]}' "$@";
}
float_to_integer_rounding_floor() {
awk 'BEGIN{for (i=1; i<ARGC;i++) printf "%d", int( ARGV[i] )}' "$@";
}