hisham hm

A small practical example where Lua’s % behavior is better than C’s

These days I saw on Twitter a comment on how the behavior of the % (remainder) operator for negative numbers is weird in C.

I’ve seen this discussion come up numerous times in the Lua mailing list over the years. The reason being because Lua does it different, and most languages simply copy the behavior of C.

Today I saw Etiene’s cool demo of a mini JavaScript Duck Hunt clone that she presented at a “intro to programming” workshop for the Women in Leadership event in Bremen, Germany.

It’s a really nice demo of game behavior in a short span of code, and with the environment of Mozilla Thimble, it instantly enticed me to play around with the code and see what happened.

The first thing that came to my attention was that the ducks spawn at position x=0, and this made them “pop” into the screen. I thought that changing the initial value to something like x=-50 would be a small change to try and would produce a smoother effect (just change 0 to -50 in lines 56 and 116).

When I first tried that, the result was that they would show up, but wouldn’t start flapping their wings until they were at x=0. The reason is because the logic to switch sprites is made testing x % 30 for values 0, 10 and 20… and JavaScript’s % operator, like C’s, returns negative remainders for negative divisors.

My quick hack solution was to calculate

   var absx = Math.abs(this.x);

(which required me a visit to DuckDuckGo to figure out how to properly say “abs(x)” in JavaScript). This made the birds enter the screen flapping their wings. Yay!

Of course, this is not something you’d want to have to explain in an “intro to programming” workshop. It would be better if the animation “just worked” with that change…

But wait! If you have really sharp eyes, you’ll notice that from -50 to 0, the birds are flapping their wings upwards and from 0 on, they do it downwards. The animation is inverted!

The reason is because operating on abs(x) causes this:

Lua 5.3.0  Copyright (C) 1994-2015 Lua.org, PUC-Rio
> for i = -50, 100 do io.write(math.abs(i)%30, " ") end
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 3 4 5 6 7 8 9 10

If I write a one-liner to simulate the sprite logic in Duck Hunt, I get this:

> for i = -50, 100 do r=math.abs(i)%30; io.write(r==0 and "1" or (r==10 and "2" or (r==20 and "3" or ".") ) ) end
3.........2.........1.........3.........2.........1.........2.........3.........1.........2.........3.........1.........2.........3.........1.........2

Indeed, it’s going 3,2,1, 3,2,1 at the negative numbers and then 1,2,3, 1,2,3 at the positive ones. But let’s just drop the math.abs in Lua and see what happens:

> for i = -50, 100 do r=i%30; io.write(r==0 and "1" or (r==10 and "2" or (r==20 and "3" or ".") ) ) end
2.........3.........1.........2.........3.........1.........2.........3.........1.........2.........3.........1.........2.........3.........1.........2

We get 1,2,3,1,2,3 all the way!

In my experience, the vast majority of times I used %, it was to tell something to “do this every X steps”, like Etiene does in her Duck Hunt. For this kind of purposes, I’m pretty convinced that Lua’s behavior for % is a lot better. It’s unfortunate that most other languages just decided to follow the example of C.

Of course, there are a million other ways to make the ducks flap their wings, with and without %, that’s not the point. But it intrigued me that, if JavaScript had Lua’s behavior for %, my initial tiny change would have “just worked”!


  1. Guilherme Berger

    Sunday, April 19, 2015 - 12:14:45

    What you want is the `mod` mathematical operator.

    http://stackoverflow.com/questions/4467539/javascript-modulo-not-behaving

  2. hisham

    Sunday, April 19, 2015 - 18:06:19

    > What you want is the `mod` mathematical operator.

    Yes, and I have it in Lua but not in C. :)

    I double-checked the name when I wrote the post, taking care to call the % operator “remainder” when talking about the C and JavaScript ones.

    This confusion is unfortunately widespread. From your link:

    > Funny that the language refs themselves call it the ‘modulus assignment operator’.
    >
    > MSDN: http://msdn.microsoft.com/en-us/library/ie/9f59bza0(v=vs.94).aspx
    >
    > Mozilla: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Arithmetic_Operators#.25_.28Modulus.29

    The MSDN even link leads to this discussion between Eich and Crockford to add yet another operator to JS with actual mod semantics.

    http://wiki.ecmascript.org/doku.php?id=strawman:modulo_operator

    Didn’t seem to go forward, though. But I don’t really follow the development of JS.

    You’re right in that there is a terminology confusion. I see people calling % “mod” all the time. Maybe it’s historical, because other languages such as Pascal that actually have a MOD operator (as in x := y mod z;) and to make matters worse ISO Pascal implements MOD as modulus and Turbo/Delphi implement it as a remainder. (Not sure if Algol or Fortran had it, could be fun to trace this historically.)

    Well, at least in Lua % is actually modulo, so we can keep reading “x % y” as “x mod y” :)

    In any case, calling this difference a mere terminology issue (as in “both are right, it’s just that % in C/JS is remainder and % is modulo”) is, IMO, a misrepresentation. Because the confusion above shows that even in C/JS people go to % looking for the modulo behavior, whereas the only complaints I’ve ever seen about the % operator in the Lua mailing list were from people who were interoperating with C and wanted the same behavior for compatibility reasons.

    My view is that Lua (and other languages, like Perl) did get this right but C and its descendants unfortunately got this wrong.

Add comment

Fill out the form below to add your own comments.

CAPTCHA imageReload imageAudible version of CAPTCHA-image