Subtracting long numbers in javascript
Asked Answered
K

6

11

Why is q == 0 in the following script?

<script>
  var start = 1234567890123456789;
  var end =   1234567890123456799;
  var q = end - start;
  alert(q);
</script>

I would think the result should be 10. What is the correct way to subtract these two numbers?

Kliber answered 12/1, 2010 at 15:37 Comment(0)
G
21

Because numbers in JavaScript are floating-point. They have limited precision.

When JavaScript sees a very long number, it rounds it to the nearest number it can represent as a 64-bit float. In your script, start and end get rounded to the same value.

alert(1234567890123456789);   // says: 1234567890123456800
alert(1234567890123456799);   // says: 1234567890123456800

There's no built-in way to do precise arithmetic on large integers, but you can use a BigInteger library such as this one.

Grapheme answered 12/1, 2010 at 15:39 Comment(0)
I
4

As of January 2020, BigInt datatype is going to be added to Javascript. The proposal is currently in Stage 4. It will enable precise calculation for number which are more than 2^53-1 (Number.MAX_SAFE_INTEGER).

BigInt has been shipped in Chrome, Node, Firefox, and is underway in Safari. Read more here.

var start = BigInt('1234567890123456789');
var end = BigInt('1234567890123456799');
var q = end - start;
alert(q)

A BigInt is created by appending n to the end of an integer literal — 10n — or by calling the function BigInt(). It is also different from Number so 1 + 1n will fail.

You can read more about it here from MDN pages

Infare answered 10/1, 2020 at 16:19 Comment(0)
W
3

Jason already posted the why. For a solution, you can get a Javascript BigInt library at http://www-cs-students.stanford.edu/~tjw/jsbn/

Walden answered 12/1, 2010 at 15:41 Comment(1)
@MicahelBray you should post the above as a comment to Jasons' answer.Femoral
C
2

const subtract = (a, b) => [a, b].map(n => [...n].reverse()).reduce((a, b) => a.reduce((r, d, i) => {
    let s = d - (b[i] || 0)
    if (s < 0) {
        s += 10
        a[i + 1]--
    }
    return '' + s + r
}, '').replace(/^0+/, ''))


Better use big-integer library for these things so as to handle all different test cases.

This is just for the a general case you can use....

Countenance answered 17/7, 2018 at 8:15 Comment(0)
F
0

It is explained in the JavaScript documentation:

According to the ECMAScript standard, there is only one number type: the double-precision 64-bit binary format IEEE 754 value (numbers between -(253-1) and 253-1). There is no specific type for integers.

Wikipedia page about double precision floating point format explains:

Between 252= 4,503,599,627,370,496 and 253= 9,007,199,254,740,992 the representable numbers are exactly the integers. For the next range, from 253 to 254, everything is multiplied by 2, so the representable numbers are the even ones, etc.

(All integer numbers smaller than 252 are represented exactly.)

1234567890123456789 and 1234567890123456799 are larger than 260= 1152921504606846976. At this magnitude only about 1% of the integer numbers are stored exactly using the double-precision floating point format.

These two cannot be stored exactly. They both are rounded to 1234567890123456800.


The JavaScript documentation also explains how to tell if a an integer number is stored exactly:

[...] and starting with ECMAScript 6, you are also able to check if a number is in the double-precision floating-point number range using Number.isSafeInteger() as well as Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER. Beyond this range, integers in JavaScript are not safe anymore and will be a double-precision floating point approximation of the value.

Filefish answered 20/1, 2018 at 9:26 Comment(0)
N
0
function add(x, y) {
    //*********************************************************************//
    // This function adds or subtracts two extremely large decimal numbers //
    // Inputs x and y should be numbers, i.e. commas are removed already   //
    // Use this function to remove commas and convert to number:           //
    // x = parseFloat(strNumber.replaceAll(",","").trim());                //
    // Inputs x and y can be both positive, or both negative,              //
    // or a combination (i.e. one positive and one negative in any         //
    // position whether as x or as y) which means subtraction              //
    //*********************************************************************//

    var temp, borrow=false, bothNeg=false, oneNeg=false, neg=false;
    if (x < 0 && y < 0) { bothNeg = true; x = -x; y = -y; } 
    else if (x < 0 || y < 0) {
        oneNeg = true;
        if (Math.abs(x) == Math.abs(y)) { x = 0; y = 0; }
        else if (x < 0 && Math.abs(x) > Math.abs(y)) { neg = true; x = -x; y = -y; }
        else if (x < 0 && Math.abs(x) < Math.abs(y)) { temp = y; y = x; x = temp; }
        else if (y < 0 && Math.abs(x) < Math.abs(y)) { neg = true; temp = y; y = -x; x = -temp; }
    }
    x = parseInt(x*1000000000/10).toString();
    y = parseInt(y*1000000000/10).toString();
    var lenx=x.length, leny=y.length, len=(lenx>leny)?lenx:leny, sum="", div=0, x1, y1, rem;
    for (var i = 0; i < len; i++) {
        x1 = (i >= lenx) ? 0 : parseInt(x[lenx-i-1]);
        y1 = (i >= leny) ? 0 : parseInt(y[leny-i-1]);
        y1 = (isNaN(y1)) ? 0 : y1;
        if (oneNeg) y1 = -y1;
        if (borrow) x1 = x1 - 1;
        if (y < 0 && x1 > 0 && Math.abs(x1) >= Math.abs(y1)) { borrow=false; div=0; }
        if (y < 0 && y1 <= 0 && (x1 < 0 || Math.abs(x1) < Math.abs(y1))) { borrow=true; rem=(x1+y1+div+10)%10; div=10; }
        else { rem=(x1+y1+div)%10; div=Math.floor((x1+y1+div)/10); }
        sum = Math.abs(rem).toString() + sum;
    }
    if (div > 0) sum = div.toString() + sum;
    sum = parseFloat(sum*10/1000000000);
    if (bothNeg || neg) sum = -sum;
    return sum;
}
Nymph answered 16/10, 2020 at 12:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.