“ mod”和“ rem”在哈斯克尔的区别

在哈斯克尔,modrem到底有什么不同?

两者似乎给出了相同的结果

*Main> mod 2 3
2
*Main> rem 2 3
2
*Main> mod 10 5
0
*Main> rem 10 5
0
*Main> mod 1 0
*** Exception: divide by zero
*Main> rem 1 0
*** Exception: divide by zero
*Main> mod 1 (-1)
0
*Main> rem 1 (-1)
0
118060 次浏览

当第二个论点是否定的时候,它们就不一样了:

2 `mod` (-3)  ==  -1
2 `rem` (-3)  ==  2

是的,这些函数的作用是不同的。正如 正式文件中所定义的:

quot是向零截断的整数除法

rem是整数余数,令人满意:

(x `quot` y)*y + (x `rem` y) == x

div是向负无穷大截断的整数除法

mod是整数模,满足:

(x `div` y)*y + (x `mod` y) == x

当你使用一个负数作为第二个参数并且结果不为零时,你可以真正注意到这种差异:

5 `mod` 3 == 2
5 `rem` 3 == 2


5 `mod` (-3) == -1
5 `rem` (-3) == 2


(-5) `mod` 3 == 1
(-5) `rem` 3 == -2


(-5) `mod` (-3) == -2
(-5) `rem` (-3) == -2

实际上:

如果知道两个操作数都是正数,通常应该使用 quotremquotRem来提高效率。

如果您不知道两个操作数都是正的,那么您必须考虑您希望结果看起来是什么样子。您可能不想要 quotRem,但是您也可能不想要 divMod(x `div` y)*y + (x `mod` y) == x定律是一个非常好的定律,但是向负无穷大(克努特式除法)舍入除法往往没有确保 0 <= x `mod` y < y(带余除法)舍入除法那么有用和有效。

如果您只想测试整除性,那么应该始终使用 rem

本质上,x `mod` y == 0相当于 x `rem` y == 0,但是 remmod快。

quotRem' a b = (q, r) where
q = truncate $ (fromIntegral a / fromIntegral b :: Rational)
r = a - b * q
divMod'  a b = (q, r) where
q = floor    $ (fromIntegral a / fromIntegral b :: Rational)
r = a - b * q

例如:

(-3) / 2 = -1.5
(-3) `quot` 2 = truncate (-1.5) = -1
(-3) `div`  2 = floor    (-1.5) = -2
(-3) `rem` 2 = -3 - 2 * (-1) = -1
(-3) `mod` 2 = -3 - 2 * (-2) = 1


3 / (-2) = -1.5
3 `quot` (-2) = truncate (-1.5) = -1
3 `div`  (-2) = floor    (-1.5) = -2
3 `rem` (-2) = 3 - (-2) * (-1) = 1
3 `mod` (-2) = 3 - (-2) * (-2) = -1

我不能上传图片来解释,但你可以自己画。

假设:

X = mod(a,b)  ; Y = rem(a,b)


---(-(n+1)b)---a---(-nb)---.......--(-2b)-----(-b)-----0-----b--->


X = a - [ -(n+1)b ]

所以 X 总是正的

Y = a - [ -nb ]

标准文件:

mod  -->  a - b.*floor(a./b).......floor is closer to negative infinity


rem  -->  a - b.*fix(a./b).........fix is closer to 0