3

Is there a simple way (hoping for a small function, not a library) to add or subtract from a 64bit integer in JavaScript?

Example 64bit int: 291270990346989568

Background: I am working with the Twitter API which has 64bit tweet ID's. I'd like to add or subtract one from those IDs to manipulate what results I get back from Twitter.

Justin
  • 26,443
  • 16
  • 111
  • 128
  • 3
    You mean like `yournumber--;` and `yournumber++;` ? – Alex W Jan 15 '13 at 21:41
  • 1
    Related: http://stackoverflow.com/questions/6041124 – Tomasz Nurkiewicz Jan 15 '13 at 21:42
  • You will have to split it into two 32-bit ints; in (current) JavaScript `291270990346989568 === 291270990346989600` you don't have that level of precision – Paul S. Jan 15 '13 at 21:45
  • @PaulS. I was thinking something along those lines. Splitting, adding or subtracting, then re-joining... The number is stored as a string, so I don't need JS to recognize the end result as an INT either. But I really don't know how to do this. – Justin Jan 15 '13 at 21:54
  • Related: http://stackoverflow.com/questions/9643626/javascript-cant-handle-64-bit-integers-can-it – JohnJohnGa Jan 15 '13 at 21:58
  • if you only want to add or remove one, store your number in the string then split the string into int array and add one to the last element and then join...? – JohnJohnGa Jan 15 '13 at 22:02
  • Doesn't Java have a `long` type which is 64-bits? Why is everyone using a pair of ints or strings? – Francis Avila Jan 15 '13 at 23:08
  • @FrancisAvila this isn't a question about _Java_. – Paul S. Jan 15 '13 at 23:09

2 Answers2

2

The following code does what you've described.

Splitting, adding or subtracting, then re-joining...

This code has both increment and decrement functions, and a test function with some edge cases. When splitting number strings and doing math on them, you need to consider what happens to "leading zeroes", so there's a padding function for that.

Thanks, Justin, for providing a JSFiddle with this solution.

/*
 * Prepend zeros to expand a given string to given length
 *
 * @var {String} numStr Number
 * @var {Number} len    Length to pad out to
 *
 * @returns {String}
 */
function pad0 (numStr,len) {
  while (numStr.length < len) {
    numStr = "0" + numStr;
  }
  return numStr
}

/*
 * Decrement the given (64 bit) integer.
 *
 * @var {String} int64  Postive non-zero integer, as a string
 *
 * @returns {String}
 */
function decrInt64 (int64) {
  var result = "";
  var midpt = Math.floor(int64.length/2);
  var upper = int64.substring(0,midpt);
  var lower = int64.substring(midpt);
  var upperVal = new Number(upper);
  var lowerVal = new Number(lower);

  if (lowerVal == 0) {
    if (upperVal == 0) {
      // We don't support negative numbers
      result = "*ERROR*"
    }
    else {
      // borrow 1
      result = pad0((--upperVal).toString(),upper.length) +
               (new Number("1"+lower) - 1).toString();
    }
  }
  else {
    var newLower = (lowerVal - 1).toString();
    result = upper + pad0(newLower,lower.length);
  }
  alert(result);
}

/*
 * Increment the given (64 bit) integer.
 *
 * @var {String} int64  Postive, as a string
 *
 * @returns {String}
 */
function incrInt64 (int64) {
  var result = "";
  var midpt = Math.floor(int64.length/2);
  var upper = int64.substring(0,midpt);
  var lower = int64.substring(midpt);
  var upperVal = new Number(upper);
  var lowerVal = new Number(lower);

  var newLower = (++lowerVal).toString();
  // Did we overflow?
  if (lower.length < newLower.length) {
    // Yes, carry the 1
    result = (++upperVal).toString() + newLower.substring(1);
  }
  else {
    result = upper + pad0(newLower,lower.length);
  }
  alert(result);
}

// Test function
window.displaymessage= function ()
{
  decrInt64("291270990046989568");
  incrInt64("291270990046989568");
  decrInt64("000000000000000000");
  incrInt64("000000000000000000");
  decrInt64("000000001000000000");
  incrInt64("000000001000000000");
  decrInt64("099999999999999999");
  incrInt64("099999999999999999");
  decrInt64("999999999999999999");
  incrInt64("999999999999999999");
}
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
1

Addition of integer-formatted strings (base 10) of indefinite lengths can be done by splitting the string into segments of 9 characters, calculating + or - for that and then moving to the preceding 9 characters. This is because the largest 9 character number, 999999999 is 32-bit safe (as is 1999999999, which I used in subtraction).

The following code has 3 functions and the word integer is assumed to mean an integer-formatted string.

  • addAsString, takes two non-negative integers x and y, returns x + y
  • subtractAsString, takes two non-negative integers x and y, |x| >= |y|, returns x - y
  • addORsub, takes any two integers x and y, returning x + y

I've tried to explain what is happening via comments in the code

// Indefinate length addition
function addAsString(x, y) { // x, y strings
    var s = '';
    if (y.length > x.length) { // always have x longer
        s = x;
        x = y;
        y = s;
    }
    s = (parseInt(x.slice(-9),10) + parseInt(y.slice(-9),10)).toString(); // add last 9 digits
    x = x.slice(0,-9); // cut off last 9 digits
    y = y.slice(0,-9);
    if (s.length > 9) { // if >= 10, add in the 1
        if (x === '') return s; // special case (e.g. 9+9=18)
        x = addAsString(x, '1');
        s = s.slice(1);
    } else if (x.length) { // if more recursions to go
        while (s.length < 9) { // make sure to pad with 0s
            s = '0' + s;
        }
    }
    if (y === '') return x + s; // if no more chars then done, return
    return addAsString(x, y) + s; // else recurse, next digit
}

// Indefinate length subtraction (x - y, |x| >= |y|)
function subtractAsString(x, y) {
    var s;
    s = (parseInt('1'+x.slice(-9),10) - parseInt(y.slice(-9),10)).toString(); // subtract last 9 digits
    x = x.slice(0,-9); // cut off last 9 digits
    y = y.slice(0,-9);
    if (s.length === 10 || x === '') { // didn't need to go mod 1000000000
        s = s.slice(1);
    } else { // went mod 1000000000, inc y
        if (y.length) { // only add if makes sense
            y = addAsString(y, '1');
        } else { // else set
            y = '1';
        }
        if (x.length) {
            while (s.length < 9) { // pad s
                s = '0' + s;
            }
        }
    }
    if (y === '') { // finished
        s = (x + s).replace(/^0+/,''); // dont return all 0s
        return s;
    }
    return subtractAsString(x, y) + s;
}

// Indefinate length addition or subtraction (via above)
function addORsub(x, y) {
    var s = '';
    x = x.replace(/^(-)?0+/,'$1').replace(/^-?$/,'0'); // -000001 = -1
    y = y.replace(/^(-)?0+/,'$1').replace(/^-?$/,'0'); // -000000 =  0
    if (x[0] === '-') { // x negative
        if (y[0] === '-') { // if y negative too
            return '-' + addAsString(x.slice(1), y.slice(1)); // return -(|x|+|y|)
        }
        return addORsub(y, x); // else swap
    }
    if (y[0] === '-') { // x positive, y negative
        s = y.slice(1);
        if (s.length < x.length || (s.length === x.length && s < x)) return subtractAsString(x, s) || '0'; // if |x|>|y|, return x-y
        if (s === x) return '0'; // equal then 0
        s = subtractAsString(s, x); // else |x|<|y|
        s = (s && '-' + s) || '0';
        return s; // return -(|y|-x)
    }
    return addAsString(x, y); // x, y positive, return x+y
}

Example usage (fiddle)

var i = addORsub('291270990346989568', '1'); // add
i === '291270990346989569';

i = addORsub('291270990346989568', '-1'); // subtract
i === '291270990346989567';
Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • i would switch your cast (+) with parseInt(x,10) – JohnJohnGa Jan 15 '13 at 22:17
  • @JohnJohnGa Yep. I could also do this in groups of 9 digits at a time.. let me edit – Paul S. Jan 15 '13 at 22:22
  • So how would I call this? Just split the INT and pass it in? Perhaps you could setup a working example in http://jsfiddle.net/ – Justin Jan 15 '13 at 22:38
  • @Justin edited in example use and to pad `s` if it is short (rare cases e.g. `1000000000+1` would have become `11`, now fixed) – Paul S. Jan 15 '13 at 22:56
  • Nice, even works for subtraction. Well freaking done. http://jsfiddle.net/bwmNU/1/ – Justin Jan 15 '13 at 23:20
  • @Justin subtraction can't be relied on without changes to the code. (What happens when it goes negative? What happens if `x` or `y` have _length_ greater than `9`? What happens if `y` is longer than `x`? etc) – Paul S. Jan 15 '13 at 23:24
  • So what would you need to change to accommodate subtraction? – Justin Jan 15 '13 at 23:58
  • 1
    @Justin Added more code for it. I know it looks messy and could probably be cleaned up, but these functions now support addition and subtraction for an integer of any length, with `addORsub` accepting positive OR negative numbers (try my fiddle with any ints, overkill galore). – Paul S. Jan 16 '13 at 01:29