21

I try to calculate with JS' modulo function, but don't get the right result (which should be 1). Here is a hardcoded piece of code.

var checkSum = 210501700012345678131468;
alert(checkSum % 97);

Result: 66

Whats the problem here?

Regards, Benedikt

6 Answers 6

20

For an IBAN calculation form a normal bankaccount number I end up with a very large number contained in a string datatype. From this large number I have to find the rest when divided by 97 -> large number % 97.

As soon as I convert the datatype to an integer I get an overflow resulting in a negative integer and eventually a wrong rest value. As I saw some verbose pieces of code (which also gave wrong outcome), I could not resist to share my own. Credits go to Finding Modulus of a Very Large Number with a Normal Number

modulo: function(divident, divisor) {
    var partLength = 10;

    while (divident.length > partLength) {
        var part = divident.substring(0, partLength);
        divident = (part % divisor) +  divident.substring(partLength);          
    }

    return divident % divisor;
}

N.B. I use 10 positions here as this is smaller than the 15 (and some) positions of max integer in JavaScript, it results in a number bigger than 97 and it's a nice round number. The first two arguments matter.

Sign up to request clarification or add additional context in comments.

2 Comments

This is by far the best solution I could find on the web. Math proof, compatible with every browser, short, easy to be read, simple to apply for IBAN check.
The hyperlink of the article in the credits was changed. devx.com/tip-bank/39012 From the article: "Suppose you need to find nBig % a: Take a manageable portion (say, x) of the input large number (nBig) and subtract it from nBig (nBig = nBig – x). Compute the mod of x (mod = x % a). Add the mod to nBig (nBig = nBig + mod). This way, the number nBig can be reduced without affecting the actual answer."
12

A bunch of improvements to Benedikt's version: cRest += '' + cDivident; is a bugfix; parseInt(divisor) makes it possible to pass both arguments as strings; check for empty string at the end makes it always return numerical values; added var statements so it's not using global variables; converted foreach to old-style for so it works in browsers with older Javascript; fixed the cRest == 0; bug (thanks @Dan.StackOverflow).

function modulo(divident, divisor) {
  let cDivident = '';
  let cRest = '';

  for (let i in divident) {
    let cChar = divident[i];
    let cOperator = cRest + '' + cDivident + '' + cChar;

    if (cOperator < parseInt(divisor)) {
      cDivident += '' + cChar;
    } else {
      cRest = cOperator % divisor;
      if (cRest == 0) {
        cRest = '';
      }
      cDivident = '';
    }
  }
  cRest += '' + cDivident;
  if (cRest == '') {
    cRest = 0;
  }
  return cRest;
}

2 Comments

Small bug/typo. The final 'cRest == 0' should really be 'cRest = 0'.
Note that this still does not work for numbers above roughly 2**50 or something. Not sure how to handle this other than using Python where 2**99999 % 7 works just fine. (Note that ** is to exponentiation, so 2**8==256.)
10

For those who simply want to copy&paste a working (functional) solution in ES6 to check IBANs:

function isIBAN(s){
    const rearranged = s.substring(4,s.length) + s.substring(0,4);
    const numeric   = Array.from(rearranged).map(c =>(isNaN(parseInt(c)) ? (c.charCodeAt(0)-55).toString() : c)).join('');
    const remainder = Array.from(numeric).map(c => parseInt(c)).reduce((remainder, value) => (remainder * 10 + value) % 97,0);

    return  remainder === 1;}

You could even write it as a one-liner.

The modulo operation is performed on the array of integers storing the actual number (divident, applied as string to function):

function modulo(divident, divisor){
   return Array.from(divident).map(c => parseInt(c)).reduce((remainder, value) => (remainder * 10 + value) % divisor,0);
};

This works because Modulo is distributive over addition, substraction and multiplication:

  • (a+b)%m = ((a%m)+(b%m))%m
  • (a-b)%m = ((a%m)-(b%m))%m
  • (ab)%m = ((a%m)(b%m))%m

The IBAN function transpiled to ES5 looks like:

function (s) {
    var rearranged = s.substring(4, s.length) + s.substring(0, 4);
    var numeric = Array.from(rearranged).map(function (c) { return (isNaN(parseInt(c)) ? (c.charCodeAt(0) - 55).toString() : c); }).join('');
    var remainder = Array.from(numeric).map(function (c) { return parseInt(c); }).reduce(function (remainder, value) { return (remainder * 10 + value) % 97; }, 0);
    return remainder === 1;
};

Comments

5

looks like you've fallen victim to this: What is JavaScript's highest integer value that a Number can go to without losing precision?

just to reiterate what's in the other thread:

they are 64-bit floating point values, the largest exact integral value is 2^53. however, from the spec section [8.5: Number Type]:

Some ECMAScript operators deal only with integers in the range −2^31 through 2^31−1, inclusive, or in the range 0 through 2^32−1, inclusive. These operators accept any value of the Number type but first convert each such value to one of 2^32 integer values. See the descriptions of the ToInt32 and ToUint32 operators in sections 0 and 0, respectively

But credit where credit is due. Jimmy got the accepted answer over there for doing the legwork (well, googling).

3 Comments

So, is there a workaround for this? I can't find anything helpful via google.
Couldn't you do a division and calculate the remainder?
given that the checksum given above is already above 2^53, you'd have to do something a little more .... interesting... to break up the number before performing any operations on it
4

Finally, my solution:

function modulo (divident, divisor) {
    cDivident = '';
    cRest = '';

    for each ( var cChar in divident ) {
        cOperator = cRest + '' + cDivident + '' + cChar;

        if ( cOperator < divisor ) {
            cDivident += '' + cChar;
        } else {
            cRest = cOperator % divisor;
            if ( cRest == 0 ) cRest = '';
            cDivident = '';
        }

    }

    return cRest;
}

Comments

3

Silent Matt has developed a Javascript library for Big Integers. It could solve this issue too.

2 Comments

But this library does not contain a modulo functionality, does it?
Image
Yes it does, see divRem or remainder

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.