11

The Euclidean division theorem, with which most math students and Haskellers are familiar, states that

Given two integers a and b, with b ≠ 0, there exist unique integers q and r such that a = bq + r and 0 ≤ r < |b|.

This gives the conventional definitions of quotient and remainder. This 1992 paper argues that they are the best ones to implement in a programming language. Why, then, does divMod always round the dividend toward negative infinity?

Exact difference between div and quot shows that divMod already does a fair bit of extra work over quotRem; it seems unlikely to be much harder to get it right.

Code

I wrote the following implementation of a Euclidean-style divMod based on the implementation in GHC.Base. I'm pretty sure it's right.

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#

Not only does this produce pleasantly Euclidean results, but it's actually simpler than the GHC code. It clearly performs at most two comparisons (as opposed to four for the GHC code).

In fact, this could probably be made entirely branchless without too much work by someone who knows more about primitives than I.

The gist of a branchless version (presumably someone who knows more could make it more efficient).

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)
Community
  • 1
  • 1
dfeuer
  • 48,079
  • 5
  • 63
  • 167
  • 10
    Haskell was designed before 1992. :) – augustss Jun 13 '14 at 17:46
  • @augustss, is this version available in some library that you know of? It looks to me like it's not only more intuitive but also probably *faster* (see the code I just added). – dfeuer Jun 13 '14 at 18:42
  • I'm having a hard time understanding what differences you are talking about. Could you give some examples of inputs and outputs that illustrate the differences between the standard library version and your version? – Dietrich Epp Jun 13 '14 at 19:27
  • @DietrichEpp, I don't know if it's quite fair to call it *my* version. I wrote the code, but the idea has been around for a long time. The differences show up with negative arguments. Try `[divMod x y|x<-[(-5)..5], y<-[(-5)..5]\\[0]]` with both the standard `divMod` and `divMod2`. – dfeuer Jun 13 '14 at 19:35
  • @dfeuer: "Your" has a variety of meanings in different contexts. I don't have access to a Haskell interpreter at this moment: could you paste the differences into the question? – Dietrich Epp Jun 13 '14 at 19:46
  • @DietrichEpp, I don't want to edit too many times, but 3/(-2)=-1.5, so rounding toward negative infinity, div 3 (-2) = -2. (-2)*(-2)=4, so the "remainder", mod 3 (-2) = -1. The version I gave gives instead div2 3 (-2) = -1, and (-1)*(-2)=2, so then mod2 3 (-2) = 1. – dfeuer Jun 13 '14 at 19:53
  • 1
    Looking at http://en.wikipedia.org/wiki/Modulo_operation, almost all languages use truncated division (marked "dividend") or floored division (marked "divisor"); very few languages use the Euclidean definition (marked "always positive"). – newacct Jun 13 '14 at 19:53
  • Okay, now I get it. The differences are for negative divisors. – Dietrich Epp Jun 13 '14 at 19:56
  • 1
    @newacct: I would expect negative dividends are probably much more common than negative divisors. Personally, I would regard division as something which, rather than being a single specifiable token `/`, should make it easy for programmers to either specify what they want or say they don't care [e.g. because the dividend is expected to always exact multiple of the divisor, or dividend and divisor are expected to always be positive]. I'd say there's probably a roughly 1000:50:1 ratio between cases where I didn't care which style was used, those where I wanted either floored or Euclidian, ... – supercat Jun 13 '14 at 20:06
  • ... or those where I wanted truncated (and ironically in the one case I wanted truncated division the compiler I was using generated horrible code for dividing a signed `int` by 2, so I had to emulate it by hand using a shift!) I can't think of any cases where I've used integer division with negative divisors. I wonder what the usage cases for that would be? – supercat Jun 13 '14 at 20:09
  • @supercat Well, this thread is tagged Haskell, which already has three different division functions (`(/)`, `div` and `quot`) for specifying what you want. And still, as this thread shows, it maybe leaves out the most "ideal" option (for integers). – Ørjan Johansen Jun 13 '14 at 21:52
  • @ØrjanJohansen: Fair point. Most of my experience is with C, which just uses one operator, and it has the least-useful meaning. As for whether floored or Euclidian is superior, I can't think of any situation where it really mattered. What's important is that proper division operator should obey (n+d)/d == (n/d)+1. – supercat Jun 13 '14 at 23:21
  • @supercat, you should check out the paper I linked to in the question. – dfeuer Jun 13 '14 at 23:26
  • @dfeuer I don't know a library that has it, but you can make one. – augustss Jun 14 '14 at 10:18
  • I can't answer the original question, probably an early design decision taken in the old days and never questioned since. But this topic matters to me. I my daily practice, I never use the mod function with negative right arguments. But for negative left arguments, I really need the floored behavior, mainly for its "translation invariance": (a + kb) mod b = (a+k'b) mod b. Lacking this function built-in is a real frustration. And implementing it from the % operator is uneasy and costly. –  Jun 18 '14 at 06:41
  • I am also missing an integer div function with the flooring behavior, complementary div and mod functions, corresponding to a ceiling (can be defined as a cdiv b ::= - (-a div b), cmod b ::= - (-a mod b)), and a "largest multiple" function, a lmu b ::= a - (a mod b). –  Jun 18 '14 at 06:49

1 Answers1

0

At this point, I'd say backwards compatibility. (See @augustss comment.) Maybe it could be changed in the next major release of the report, but you'd have to convince the haskell-prime committee and possibly the GHC developers.