为什么divMod会向下舍入而不是确保正余数?

时间:2014-06-13 16:28:34

标签: haskell integer-arithmetic

大多数数学学生和Haskeller都熟悉的Euclidean division theorem表示

  

给定两个整数a和b,其中b≠0,存在唯一的整数q和r,使得a = bq + r且0≤r<1。 | B |

这给出了商和余数的常规定义。这个1992 paper认为它们是用编程语言实现的最好的。那么,为什么divMod总是将红利围绕负无穷大?

Exact difference between div and quot表明divMod已经在quotRem上做了相当多的额外工作;似乎不太可能更难做到正确。

代码

我根据divMod中的实现编写了以下欧几里德式GHC.Base的实现。我很确定它是对的。

divModInt2 :: Int -> Int -> (Int, Int)
divModInt2 (I# x) (I# y) = case (x `divModInt2#` y) of
                        divModInt2# :: Int# -> Int# -> (# Int#, Int# #)

x# `divModInt2#` y#
 | (x# <# 0#) = case (x# +# 1#) `quotRemInt#` y# of
                    (# q, r #) -> if y# <# 0#
                                  then (# q +# 1#, r -# y# -# 1# #)
                                  else (# q -# 1#, r +# y# -# 1# #)
 | otherwise  = x# `quotRemInt#` y#

这不仅会产生令人愉快的欧几里德结果,而且它实际上比GHC代码更简单 。它显然最多执行两次比较(而不是GHC代码的四次比较)。

事实上,如果没有太多关于原始人的工作而不是我的工作,这可能完全没有分支。

无分支版本的要点(可能是知道更多的人可以提高效率)。

x `divMod` y = (q + yNeg, r - yNeg * y - xNeg)
  where
    (q,r) = (x + xNeg) `quotRem` y
    xNeg = fromEnum (x < 0)
    yNeg = xNeg*(2 * fromEnum (y < 0) - 1)

1 个答案:

答案 0 :(得分:0)

此时,我说后向兼容性。 (请参阅@augustss评论。)也许可以在报告的下一个主要版本中进行更改,但您必须说服haskell-prime委员会和可能的GHC开发人员。