我想知道从编程语言/编译器实现的角度来看,使用截断朝负无穷大(Haskell,Ruby)而不是截断朝零(C,PHP)有什么好处。
似乎截断到负无穷大是正确的方法,但我还没有找到这种说法的可靠来源,也没有找到这样的决定如何影响编译器的实现。我对可能的编译器优化特别感兴趣,但不仅限于此。
相关来源:
哈斯克尔分区
quotRem和divMod之间的区别什么时候有用?
这些实际上甚至不是唯一的选择,事实上,甚至可能不是最好的选择。我可以在这里总结一下,但也许最好只链接到这篇对比截断、地板和欧几里得除法的优秀论文,涵盖理论和一些现实世界的应用,欧几里得函数的定义div 和 mod,雷蒙德 T. 布特。
以下是 ISO/IEC 10967-1:2012 语言独立算术 (vl.LIA-1) C.5.1.2.2.省略。。。由我插入。
。通常使用两个舍入规则:舍入到负无穷大(引用I)和舍入到零。后者在 LIA-1 中没有指定,因为当参数具有不同的符号时容易错误使用。例如
quotI(-3,2) = -2 向负无穷大四舍五入,在 LIA-1 中指定
divtI(-3,2) = -1 向零舍入,不再由 LIA 的任何部分指定
我...以及...都满足一个广泛有用的翻译不变量:
quot I(x + i * y, y)= quotI( x, y) + i 如果 y ≠ 0,并且没有溢出发生
。quotI是许多数学家喜欢的整数除法形式。divtI(不再由LIA指定)是Fortran引入的划分形式。
整数除法经常用于分组。例如,如果要将一系列索引项划分为由 n 个项组成的组,则自然会将项
i
放入组i/n
中。如果 quot I 用于整数除法,则此方法工作正常。但是,如果使用 divtI(在 LIA 中不再指定),并且i
可以为负数,则组 0 将获得 2 ⋅ n-1 项,而不是所需的 n。这种负面i
的不均匀行为可能会导致细微的程序错误,并且是反对使用 divtI 的有力理由......
除法中不同类型的舍入有多种权衡。正如杰克·麦克阿瑟(Jake McArthur)所提到的,这不是唯一的。例如,还有四舍五入到最接近的整数。
另一个考虑因素是整数除法和余数是齐头并进的。quotient * divisor + remainder = dividend
法成立。因此,不同类型的除法舍入将产生不同类型的余数。例如:
- 除
- 法四舍五入到零,产生的余数始终与除数具有相同的符号。例如,在 C 和 Java 中,
(-5) % 3 = -2
(因为(-5) / 3 = -1
和(-1) * 3 + (-2) = -5
);而5 % (-3) = 2
(因为5 / (-3) = -1
和(-1) * (-3) + 2 = 5
)。 - 四舍五入到负无穷大,产生一个余数,该余数始终与除数具有相同的符号。例如,在 Python 和 Ruby 中,
(-5) % 3 = 1
(因为(-5) / 3 = -2
和(-2) * 3 + 1 = -5
);而5 % (-3) = -1
(因为5 / (-3) = -2
和(-2) * (-3) + (-1) = 5
)。 - 除法四舍五入到最接近的整数,将产生最小(最接近零)的余数(在两个可能的余数中)。
拥有与除数相同符号的余数通常在数学和计算机科学中最有用。人们经常需要使用固定除数计算余数。例如,x % 2
.在余数具有除数符号的语言中,例如 C 和 Java,此表达式的计算结果可以是 -1、0 或 1,具体取决于x
。但是,在余数具有除数符号的语言中,例如Python和Ruby,此表达式只能计算为0(如果偶数)或1(如果奇数)。这可能更符合意图。
我相信许多处理器架构,包括 x86 架构,都包含整数除法指令,该指令舍入为零。因此,在大多数计算机上计算此值可能更有效。我不确定是否有整数除法的指令四舍五入到负无穷大。