Modulus and Remainder are not the same

Sep12
Missing Image
By SiteCrafting Staff

This last week I was working on some logic that required circular indexing. The specifics aren't important, so I'll give an analogues example; A slideshow that slides in a rotation in this order: 1, 2, 3, 4, 1, 2… This is a very common requirement that can easily be solved using the Modulus (mod) mathematical function.

Before I explain mod I need to explain remainder: a similar mathematical function that is usually taught to 4th graders. Remainder is the left overs after dividing two numbers and rounding down to the nearest integer. For example, if you divide 6 by 4, 4 goes in once and you have two left over, and the answer is 1 remainder 2. So, 6 / 4 = 1 r 2 and 3 / 4 = 0 r 3. This turns out to be very useful in these circular indexing problems because you can just keep incrementing a number and use the remainder as an index. In our example, it would be the index into the slides.

The problem with remainder is when you go backwards and use negative numbers. -1 / 4 = 0 r -1, -6 / 4 = -1 r -2. If you had a previous button on your slideshow this problem would show up right away. Here is how remainder works on both positive and negative scales (using 4 as the denominator).



This is where mod comes in. Modulus works just like remainder on the positive scale, but it works a little differently in the negative scale. When it is negative it will keep the same tick marks as the positive. -1 mod 4 = 3, -6 mod 4 = 2. The number line for Modulus would look like this.



This is exactly the logic we are looking for! The ordering of the slideshow doesn't change just because we are in negative space. That just doesn't make sense. So, let's try using the mod (%) operator in JavaScript to make a circular indexed slideshow:

function SlideShow(slides) {
    var counter = 0;
    
    this.prev = function () {
        counter -= 1;
        transition();
    };

    this.next = function () {
        counter += 1;
        transition();
    };

    var transition = function () {
        var index = counter % slides.length;
        var slide = slides[index];
        // Do Transition
    };
}

This works great, except it doesn't return integers. The mod operator returns a float, not an integer. So we would need to do an extra step to clean up the results. This is how the code would look:

function SlideShow(slides) {
    var counter = 0;

    this.prev = function () {
        counter -= 1;
        transition();
    };

    this.next = function () {
        counter += 1;
        transition();
    };
 
    var transition = function () {
        var index = mod(counter, slides.length);
        var slide = slides[index];
        // Do Transition
    };

    var mod = function (num, denom) {
        if(num > 0) return Math.floor(num % denom);
        else return Math.ceil(num % denom);
    };

}

If you were to run this through you'd find the slideshow doesn't work in the negative. Why is that? The % operator is not mod, even though that's how everyone reads it. It's actually remainder. But we need mod, so what you really would need to write is this:

function SlideShow(slides) {
    var counter = 0;

    this.prev = function () {
        counter -= 1;
        transition();
    };

    this.next = function () {
        counter += 1;
        transition();
    };

    var transition = function () {
        var index = mod(counter, slides.length);
        var slide = slides[index];
        // Do Transition
    };

    var mod = function (num, mod) {
        var remain = num % mod;
        return Math.floor(remain >= 0 ? remain : remain + mod);
    };
}

Below are some functions that JavaScript left out: mod, div, and remainder. Div is like dividing, except it returns the integer quotient less the remainder. These functions are coded a bit differently then the example to save bytes.

function mod(num, mod) {
    var remain = num % mod;
    return Math.floor(remain >= 0 ? remain : remain + mod);
}

function remain(num, denom) {
    return Math[num > 0 ? 'floor' : 'ceil'](num % denom);
}

function div(num, denom) {
    return Math[num > 0 ? 'floor' : 'ceil'](num / denom);
}

In conclusion, circular indexes are really handy for looping on ordered sets. A really simple way of creating circular indexes is to use a mod of a counter as the index into the set. In most languages, the mod operator % is actually float remainder, which has it's problems when dealing with negative numbers. It is useful to learn how these functions work because they have properties that will simplify your code (et. al. by reducing edge cases).
 

Dev

Back To Feed