awk中的整数除法

38

我想在 awk 中使用整数除法来计算两个数字的商,即截断结果。例如:

k = 3 / 2
print k

应该打印1

根据手册

除法;因为awk中的所有数字都是浮点数,所以结果不会四舍五入为整数

有没有什么解决办法可以得到一个整数值?

原因是我想要获得一个具有整数索引[0到num-1]的数组的中间元素

3个回答

58

使用int函数获取结果的整数部分,向0截断。这将产生距离结果和0之间最近的整数。例如,int(3/2)是1,int(-3/2)是-1。

来源:AWK手册-数字函数


1
这不算整数除法,整数除法基于余数有严格的保证。这只是“截断”。 - Alec Teal

15

在简单情况下,您可以安全地使用int()函数,该函数向零截断:

awk 'BEGIN { print int(3 / 2) }'    # prints 1
gawk 'BEGIN { print int(-3 / 2) }'  # prints -1; not guaranteed in POSIX awk

请记住,awk始终使用双精度浮点数2和浮点运算3。要获取整数和整数运算的唯一方法是使用外部工具,例如标准的expr实用程序:

awk 'BEGIN { "expr 3 / 2" | getline result; print result; }'    # prints 1

这真的很尬,又长又慢,但安全可靠且易于移植。


1POSIX awk中,仅对正参数保证截断为零:int(x) — 返回将参数截断为整数的值。当 x>0 时,截断应向 0 进行。 GNU awk (gawk) 即使对于负数也使用向 0 截断:int(x) — 返回最接近 x 且位于 x 和零之间并向零截断的整数。例如,int(3) 是 3,int(3.9) 是 3,int(-3.9) 是 -3,int(-3) 也是 -3。
2 数值表达式在 POSIX 的Expressions in awk 中指定为双精度浮点数。
3 所有算术运算都应遵循 ISO C 标准规定的浮点运算语义(请参见Concepts Derived from the ISO C Standard)。POSIX awk:算术函数


如果您选择使用浮点数,应该了解它们的怪癖并准备识别和避免相关的错误。以下是几个令人恐惧的例子:

  • 不可表示的数字:

    awk 'BEGIN { x = 0.875; y = 0.425; printf("%0.17g, %0.17g\n", x, y) }'
    # prints 0.875, 0.42499999999999999
    
  • 截断误差的累积:

  • awk 'BEGIN{s=0; for(i=1;i<=100000;i++)s+=0.3; printf("%.10f, %d\n",s,int(s))}'
    # prints 29999.9999999506, 29999
    
  • 舍入误差会破坏比较:

    awk 'BEGIN { print (0.1 + 12.2 == 12.3) }'    # prints 0
    
  • 精度随着数量级的增加而降低,导致无限循环:

  • awk 'BEGIN { for (i=10^16; i<10^16+5; i++) printf("%d\n", i) }'
    # prints 10000000000000000 infinitely many times
    

阅读更多有关浮点数如何工作的内容:

  1. Stack Overflow 标签 wiki

  2. Wikipedia文章 浮点数

  3. GNU awk 任意精度算术 – 包含特定实现和一般知识的信息


我想克服浮点数误差(对于相对较小的数字)的一种方法是执行int(3/2+0.25) - user000001
@user000001 添加一个常量并不能解决问题,实际上会增加新的问题。awk 'BEGIN {print int(7/8), int (7/8 + 0.25)}' 会产生 0 1 的结果。 - Palec
是的,它必须小于1 /(d / 2),其中d是分母。只要这个值大于浮点误差,它就可以正常工作。 - user000001

6

使用以下方法可以进行安全和快速的awk整数除法:

q=(n-n%d)/d+(n<0)

+1。聪明的技巧,我得承认。不过我想知道它在精度误差方面是否安全,就像@Palec所解释的那样... - user000001
+1 这实现了一种向正无穷取整的 ceil 类型舍入。存在其他数学上正确的取模概念和因此舍入方法。可能有其他更优选的方法。 - user8017719
@sorontar 至少在我的系统中,这与 ceil 函数不等价。 - Marc.2377
使用 Gawk 5.1 的命令 gawk -v n=-1 -v d=1 'BEGIN{print(n-n%d)/d+(n<0)}' 输出的结果是 0,而在 bash -c 'echo $((-1/1))' 中输出的则是 -1。 - xebeche

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接