Skip to content
Browse files

Made Int64's behave more like native Numbers. Values that won't be in…

…teger-precise as native Number now have value of +/- Infinity
  • Loading branch information...
1 parent 7e91e8c commit 39791af5a55b60bc6b3e0e1247168ef6aea2e746 @broofa committed Mar 7, 2011
Showing with 139 additions and 58 deletions.
  1. +55 −42 Int64.js
  2. +68 −2 README.md
  3. +2 −2 package.json
  4. +14 −12 test.js
View
97 Int64.js
@@ -1,22 +1,29 @@
/**
* Support for handling 64-bit int numbers in Javascript (node.js)
*
-* JS Numbers are IEEE-754 double-precision floats, which limits the range of
-* integer values that can be accurately represented to +/- 2^^53.
+* JS Numbers are IEEE-754 binary double-precision floats, which limits the
+* range of values that can be represented with integer precision to:
+*
+* 2^^53 <= N <= 2^53
*
* Int64 objects wrap a node Buffer that holds the 8-bytes of int64 data. These
-* objects operate directly on the buffer, which means that if they are created
-* using an existing buffer, setting the value will modify the Buffer and
+* objects operate directly on the buffer which means that if they are created
+* using an existing buffer then setting the value will modify the Buffer, and
* vice-versa.
+*
+* For details about IEEE-754 see:
+* http://en.wikipedia.org/wiki/Double_precision_floating-point_format
*/
// Useful masks and values for doing bit twiddling
var MASK31 = 0x7fffffff, VAL31 = 0x80000000;
var MASK32 = 0xffffffff, VAL32 = 0x100000000;
// Map for converting hex octets to strings
-var _HEX = [];
-for (var i = 0; i < 256; i++) _HEX[i] = (i > 0xF ? '' : '0') + i.toString(16);
+var _HEX = [], _PADHEX = [];
+for (var i = 0; i < 256; i++) {
+ _HEX[i] = (i > 0xF ? '' : '0') + i.toString(16);
+}
//
// Int64
@@ -91,11 +98,11 @@ Int64.prototype = {
}
}
- // TODO: Do we want to throw if hi/lo is outside int32 range here?
+ // Technically we should throw if hi/lo is outside int32 range here, but
+ // it's not worth the effort.
// Copy bytes to buffer
- b = this.buffer;
- var o = this.offset;
+ var b = this.buffer, o = this.offset;
for (var i = 7; i >= 0; i--) {
b[o+i] = lo & 0xff;
lo = i == 4 ? hi : lo >>> 8;
@@ -106,49 +113,55 @@ Int64.prototype = {
},
/**
- * Return the approximate error involved in converting the current value to a
- * native JS number. If > 0, the value is outside the range JS can represent
- * to integer precision.
- */
- error: function() {
- return Math.ceil(Math.abs(this.valueOf()) / Int64.MAX_INT) - 1;
- },
-
- /**
- * Convert to a JS Number.
- *
- * Be aware that if the returned value is outside the range ...
- *
- * Int64.MIN_INT <= x <= Int64.MAX_INT
- *
- * ... it is unlikely to exactly represent the underlying 64-bit value.
+ * Convert to a JS Number. Returns +/-Infinity for values that can't be
+ * represented to integer precision.
*/
valueOf: function() {
var b = this.buffer, o = this.offset;
- var negate = b[0] & 0x80, x = 0, xx = 1;
- if (negate) this._2scomp();
- for (var i = o + 7; i >= o; i--) {
- var v = b[i] & (i == 0 ? 0x7f : 0xff);
- x += v*xx;
- xx *= 0x100;
+
+ var negate = b[0] & 0x80, x = 0, carry = 1;
+ for (var i = 0, ii = o + 7; i < 8; i++, ii--) {
+ var v = b[ii];
+ // Do a running 2's complement for negative numbers
+ if (negate) {
+ v = (v ^ 0xff) + carry;
+ carry = v >> 8;
+ }
+
+ x += (v & 0xff) * Math.pow(256, i);
}
- if (negate) {
- x = -x;
- this._2scomp();
+
+ // Return Infinity if we've lost integer precision
+ if (x >= Int64.MAX_INT) {
+ return negate ? -Infinity : Infinity;
}
- return x;
+
+ return negate ? -x : x;
+ },
+
+ /**
+ * Return string value
+ */
+ toString: function(radix) {
+ return this.valueOf().toString(radix || 10);
},
/**
- * Get value as a string of hex octets
- *
- * @param sep (String) string to join() with. Default=''
+ * Return a string showing the buffer octets, with MSB on the left.
*/
- toString: function(sep) {
- var b = this.buffer, o = this.offset, s = ['0x'];
+ toOctetString: function(sep) {
+ var out = new Array(8);
+ var b = this.buffer, o = this.offset;
for (var i = 0; i < 8; i++) {
- s.push(_HEX[this.buffer[o+i]]);
+ out[i] = _HEX[b[o+i]];
}
- return s.join('');
+ return out.join(sep || '');
+ },
+
+ /**
+ * Pretty output in console.log
+ */
+ inspect: function() {
+ return '[Int64 value:' + this + ' octets:' + this.toOctetString(' ') + ']';
}
};
View
70 README.md
@@ -1,3 +1,69 @@
-JavaScript Numbers are represented as [IEEE 754 double-precision floats](http://steve.hollasch.net/cgindex/coding/ieeefloat.html). This means they can only accurately represent integers in the range of -2^^53 &lt;= x &lt;= +2^^53. For projects that need to work with 64-bit ints, such as [node-thrift](https://github.com/wadey/node-thrift), a convenient way of representing is needed.
+JavaScript Numbers are represented as [IEEE 754 double-precision floats](http://steve.hollasch.net/cgindex/coding/ieeefloat.html). Unfortunately, this means they lose integer precision for values beyond +/- 2^^53. For projects that need to accurately handle 64-bit ints, such as [node-thrift](https://github.com/wadey/node-thrift), a performant, Number-like class is needed. Int64 is that class.
-Int64 attempts to fill that need.
+Int64 instances look and feel much like JS-native Numbers. By way of example ...
+
+ // First, let's illustrate the problem ...
+ > (0x123456789).toString(16)
+ '123456789' // <- what we expect.
+ > (0x123456789abcdef0).toString(16)
+ '123456789abcdf00' // <- Ugh! JS doesn't do big ints. :(
+
+ // So let's create a couple Int64s using the above values ...
+
+ // Require, of course
+ > Int64 = require('node-int64')
+
+ // x's value is what we expect (the decimal value of 0x123456789)
+ > x = new Int64(0x123456789)
+ [Int64 value:4886718345 octets:00 00 00 01 23 45 67 89]
+
+ // y's value is Infinity because it's outside the range of integer
+ // precision. But that's okay - it's still useful because it's internal
+ // representation (octets) is what we passed in
+ > y = new Int64('123456789abcdef0')
+ [Int64 value:Infinity octets:12 34 56 78 9a bc de f0]
+
+ // Let's do some math. Int64's behave like Numbers. (Sorry, Int64 isn't
+ // for doing 64-bit integer arithmetic (yet) - it's just for carrying
+ // around int64 values
+ > x + 1
+ 4886718346
+ > y + 1
+ Infinity
+
+ // Int64 string operations ...
+ > 'value: ' + x
+ 'value: 4886718345'
+ > 'value: ' + y
+ 'value: Infinity'
+ > x.toString(2)
+ '100100011010001010110011110001001'
+ > y.toString(2)
+ 'Infinity'
+
+ // Use JS's isFinite() method to see if the Int64 value is in the
+ // integer-precise range of JS values
+ > isFinite(x)
+ true
+ > isFinite(y)
+ false
+
+ // Get an octet string representation. (Yay, y is what we put in!)
+ > x.toOctetString()
+ '0000000123456789'
+ > y.toOctetString()
+ '123456789abcdef0'
+
+ // Finally, some other ways to create Int64s ...
+
+ // Pass hi/lo words
+ > new Int64(0x12345678, 0x9abcdef0)
+ [Int64 value:Infinity octets:12 34 56 78 9a bc de f0]
+
+ // Pass a Buffer
+ > new Int64(new Buffer([0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0]))
+ [Int64 value:Infinity octets:12 34 56 78 9a bc de f0]
+
+ // Pass a Buffer and offset
+ > new Int64(new Buffer([0,0,0,0,0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0]), 4)
+ [Int64 value:Infinity octets:12 34 56 78 9a bc de f0]
View
4 package.json
@@ -2,11 +2,11 @@
"name" : "node-int64",
"description" : "Support for representing 64-bit integers in JavaScript",
"url" : "http://github.com/broofa/node-int64",
- "keywords" : ["math", "integer"],
+ "keywords" : ["math", "integer", "int64"],
"author" : "Robert Kieffer <robert@broofa.com>",
"contributors" : [],
"dependencies" : [],
"lib" : ".",
"main" : "./Int64.js",
- "version" : "0.1.0"
+ "version" : "0.2.0"
}
View
26 test.js
@@ -1,24 +1,26 @@
var Int64 = require('./Int64');
var args = [
- [0],
- [1],
- [-1],
- [1e18],
- ['0ff1234500654321'],
- [0xff12345, 0x654321],
- ['0x0001234500654321'],
- ['0xFFFFFFFFFFFFFFFF']
+ [0], '0000000000000000',
+ [1], '0000000000000001',
+ [-1], 'ffffffffffffffff',
+ [1e18], '0de0b6b3a7640000',
+ ['0ff1234500654321'], '0ff1234500654321',
+ [0xff12345, 0x654321], '0ff1234500654321',
+ ['0x0000123450654321'], '0000123450654321',
+ ['0xFFFFFFFFFFFFFFFF'], 'ffffffffffffffff'
];
-for (var i = 0; i < args.length; i++) {
- var a = args[i];
+for (var i = 0; i < args.length; i += 2) {
+ var a = args[i], octets = args[i+1];
// Create instance
var x = new Int64();
Int64.apply(x, a);
- console.log(' args: ' + a);
- console.log('value: ' + x + ', string: ' + x.toString() + ', err: ' + x.error());
+ console.log('new Int64(' + a + ')');
+ var pass = x.toOctetString() == octets;
+ console.log((pass ? 'PASS' : 'FAIL') + ' - value:' + x +
+ ', octets: ' + x.toOctetString());
console.log('-----------');
}

0 comments on commit 39791af

Please sign in to comment.
Something went wrong with that request. Please try again.