From ec813a5df1a49c147ead09f043d97fdadb814ad3 Mon Sep 17 00:00:00 2001 From: Daniel Wirtz Date: Mon, 9 Dec 2013 19:30:18 +0100 Subject: [PATCH] Notes on NaN --- Long.js | 22 ++++++++++++++-------- Long.min.js | 28 ++++++++++++++-------------- README.md | 1 + docs/Long.html | 4 +++- package.json | 2 +- tests/suite.js | 22 ++++++++++++++++++++++ 6 files changed, 55 insertions(+), 24 deletions(-) diff --git a/Long.js b/Long.js index f83f845..339e8ad 100644 --- a/Long.js +++ b/Long.js @@ -53,9 +53,10 @@ * case would often result in infinite recursion. * * @exports Long + * @class A Long class for representing a 64-bit two's-complement integer value. * @param {number} low The low (signed) 32 bits of the long. * @param {number} high The high (signed) 32 bits of the long. - * @param {boolean=} unsigned Whether unsigned or not. Defaults to false (signed). + * @param {boolean=} unsigned Whether unsigned or not. Defaults to `false` (signed). * @constructor */ var Long = function(low, high, unsigned) { @@ -107,13 +108,14 @@ * @expose */ Long.fromInt = function(value, unsigned) { + var obj, cachedObj; if (!unsigned) { value = value | 0; if (-128 <= value && value < 128) { - var cachedObj = INT_CACHE[value]; + cachedObj = INT_CACHE[value]; if (cachedObj) return cachedObj; } - var obj = new Long(value, value < 0 ? -1 : 0, false); + obj = new Long(value, value < 0 ? -1 : 0, false); if (-128 <= value && value < 128) { INT_CACHE[value] = obj; } @@ -121,10 +123,10 @@ } else { value = value >>> 0; if (0 <= value && value < 256) { - var cachedObj = UINT_CACHE[value]; + cachedObj = UINT_CACHE[value]; if (cachedObj) return cachedObj; } - var obj = new Long(value, (value | 0) < 0 ? -1 : 0, true); + obj = new Long(value, (value | 0) < 0 ? -1 : 0, true); if (0 <= value && value < 256) { UINT_CACHE[value] = obj; } @@ -201,7 +203,10 @@ if (str.length == 0) { throw(new Error('number format error: empty string')); } - if (typeof unsigned == 'number') { // For goog.math.Long compatibility + if (str === "NaN" || str === "Infinity" || str === "+Infinity" || str === "-Infinity") { + return Long.ZERO; + } + if (typeof unsigned === 'number') { // For goog.math.Long compatibility radix = unsigned; unsigned = false; } @@ -370,13 +375,14 @@ if (this.isZero()) { return '0'; } + var rem; if (this.isNegative()) { // Unsigned Longs are never negative if (this.equals(Long.MIN_SIGNED_VALUE)) { // We need to change the Long value before it can be negated, so we remove // the bottom-most digit in this base and then recurse to do the rest. var radixLong = Long.fromNumber(radix); var div = this.div(radixLong); - var rem = div.multiply(radixLong).subtract(this); + rem = div.multiply(radixLong).subtract(this); return div.toString(radix) + rem.toInt().toString(radix); } else { return '-' + this.negate().toString(radix); @@ -386,7 +392,7 @@ // Do several (6) digits each time through the loop, so as to // minimize the calls to the very expensive emulated div. var radixToPower = Long.fromNumber(Math.pow(radix, 6)); - var rem = this; + rem = this; var result = ''; while (true) { var remDiv = rem.div(radixToPower); diff --git a/Long.min.js b/Long.min.js index a130ae6..a1be1ab 100644 --- a/Long.min.js +++ b/Long.min.js @@ -9,18 +9,18 @@ see: https://code.google.com/p/closure-library/ for details */ var p=!1; -(function(r){function b(a,b,d){this.low=a|0;this.high=b|0;this.unsigned=!!d}var s={},t={};b.fromInt=function(a,c){if(c){a>>>=0;if(0<=a&&256>a&&(d=t[a]))return d;d=new b(a,0>(a|0)?-1:0,!0);0<=a&&256>a&&(t[a]=d)}else{a|=0;if(-128<=a&&128>a){var d=s[a];if(d)return d}d=new b(a,0>a?-1:0,p);-128<=a&&128>a&&(s[a]=d)}return d};b.fromNumber=function(a,c){c=!!c;return isNaN(a)||!isFinite(a)?b.ZERO:!c&&a<=-u?b.MIN_SIGNED_VALUE:c&&0>=a?b.MIN_UNSIGNED_VALUE:!c&&a+1>=u?b.MAX_SIGNED_VALUE:c&&a>=v?b.MAX_UNSIGNED_VALUE:0> -a?b.fromNumber(-a,p).negate():new b(a%l|0,a/l|0,c)};b.fromBits=function(a,c,d){return new b(a,c,d)};b.from28Bits=function(a,c,d,e){return b.fromBits(a|c<<28,c>>>4|d<<24,e)};b.fromString=function(a,c,d){if(0==a.length)throw Error("number format error: empty string");"number"==typeof c&&(d=c,c=p);d=d||10;if(2>d||36f?(f=b.fromNumber(Math.pow(d,f)),e=e.multiply(f).add(b.fromNumber(k))):(e=e.multiply(c),e=e.add(b.fromNumber(k)))}return e};var l=4294967296,v=l*l,u=v/2,w=b.fromInt(16777216);b.ZERO=b.fromInt(0);b.ONE=b.fromInt(1);b.NEG_ONE=b.fromInt(-1);b.MAX_SIGNED_VALUE=b.fromBits(-1,2147483647,p);b.MAX_UNSIGNED_VALUE=b.fromBits(-1,-1,!0);b.MAX_VALUE=b.MAX_SIGNED_VALUE;b.MIN_SIGNED_VALUE= -b.fromBits(0,-2147483648,p);b.MIN_UNSIGNED_VALUE=b.fromBits(0,0,!0);b.MIN_VALUE=b.MIN_SIGNED_VALUE;b.prototype.toInt=function(){return this.unsigned?this.low>>>0:this.low};b.prototype.toNumber=function(){return this.unsigned?(this.high>>>0)*l+(this.low>>>0):this.high*l+(this.low>>>0)};b.prototype.toString=function(a){a=a||10;if(2>a||36f.length;)f="0"+f;e=""+f+e}};b.prototype.getHighBits=function(){return this.high};b.prototype.getHighBitsUnsigned=function(){return this.high>>>0};b.prototype.getLowBits=function(){return this.low};b.prototype.getLowBitsUnsigned=function(){return this.low>>> -0};b.prototype.getNumBitsAbs=function(){if(this.isNegative())return this.equals(b.MIN_SIGNED_VALUE)?64:this.negate().getNumBitsAbs();for(var a=0!=this.high?this.high:this.low,c=31;0this.high};b.prototype.isOdd=function(){return 1==(this.low&1)};b.prototype.equals=function(a){return this.unsigned!=a.unsigned&&this.high>>>31!=a.high>>> -31?p:this.high==a.high&&this.low==a.low};b.prototype.notEquals=function(a){return!this.equals(a)};b.prototype.lessThan=function(a){return 0>this.compare(a)};b.prototype.lessThanOrEqual=function(a){return 0>=this.compare(a)};b.prototype.greaterThan=function(a){return 0>> -0>this.high>>>0||a.high==this.high&&a.low>>>0>this.low>>>0?-1:1:this.subtract(a).isNegative()?-1:1};b.prototype.negate=function(){return!this.unsigned&&this.equals(b.MIN_SIGNED_VALUE)?b.MIN_SIGNED_VALUE:this.not().add(b.ONE)};b.prototype.add=function(a){var c=this.high>>>16,d=this.high&65535,e=this.low>>>16,g=a.high>>>16,f=a.high&65535,k=a.low>>>16,q;q=0+((this.low&65535)+(a.low&65535));a=0+(q>>>16);a+=e+k;e=0+(a>>>16);e+=d+f;d=0+(e>>>16);d=d+(c+g)&65535;return b.fromBits((a&65535)<<16|q&65535,d<< -16|e&65535,this.unsigned)};b.prototype.subtract=function(a){return this.add(a.negate())};b.prototype.multiply=function(a){if(this.isZero()||a.isZero())return b.ZERO;if(this.equals(b.MIN_VALUE))return a.isOdd()?b.MIN_VALUE:b.ZERO;if(a.equals(b.MIN_VALUE))return this.isOdd()?b.MIN_VALUE:b.ZERO;if(this.isNegative())return a.isNegative()?this.negate().multiply(a.negate()):this.negate().multiply(a).negate();if(a.isNegative())return this.multiply(a.negate()).negate();if(this.lessThan(w)&&a.lessThan(w))return b.fromNumber(this.toNumber()* -a.toNumber(),this.unsigned);var c=this.high>>>16,d=this.high&65535,e=this.low>>>16,g=this.low&65535,f=a.high>>>16,k=a.high&65535,q=a.low>>>16;a=a.low&65535;var n,h,m,l;l=0+g*a;m=0+(l>>>16);m+=e*a;h=0+(m>>>16);m=(m&65535)+g*q;h+=m>>>16;m&=65535;h+=d*a;n=0+(h>>>16);h=(h&65535)+e*q;n+=h>>>16;h&=65535;h+=g*k;n+=h>>>16;h&=65535;n=n+(c*a+d*q+e*k+g*f)&65535;return b.fromBits(m<<16|l&65535,n<<16|h,this.unsigned)};b.prototype.div=function(a){if(a.isZero())throw Error("division by zero");if(this.isZero())return b.ZERO; -if(this.equals(b.MIN_SIGNED_VALUE)){if(a.equals(b.ONE)||a.equals(b.NEG_ONE))return min;if(a.equals(b.MIN_VALUE))return b.ONE;var c=this.shiftRight(1).div(a).shiftLeft(1);if(c.equals(b.ZERO))return a.isNegative()?b.ONE:b.NEG_ONE;var d=this.subtract(a.multiply(c));return c.add(d.div(a))}if(a.equals(b.MIN_VALUE))return b.ZERO;if(this.isNegative())return a.isNegative()?this.negate().div(a.negate()):this.negate().div(a).negate();if(a.isNegative())return this.div(a.negate()).negate();for(var e=b.ZERO,d= -this;d.greaterThanOrEqual(a);){for(var c=Math.max(1,Math.floor(d.toNumber()/a.toNumber())),g=Math.ceil(Math.log(c)/Math.LN2),g=48>=g?1:Math.pow(2,g-48),f=b.fromNumber(c,this.unsigned),k=f.multiply(a);k.isNegative()||k.greaterThan(d);)c-=g,f=b.fromNumber(c,this.unsigned),k=f.multiply(a);f.isZero()&&(f=b.ONE);e=e.add(f);d=d.subtract(k)}return e};b.prototype.modulo=function(a){return this.subtract(this.div(a).multiply(a))};b.prototype.not=function(){return b.fromBits(~this.low,~this.high,this.unsigned)}; -b.prototype.and=function(a){return b.fromBits(this.low&a.low,this.high&a.high,this.unsigned)};b.prototype.or=function(a){return b.fromBits(this.low|a.low,this.high|a.high,this.unsigned)};b.prototype.xor=function(a){return b.fromBits(this.low^a.low,this.high^a.high,this.unsigned)};b.prototype.shiftLeft=function(a){a&=63;if(0==a)return this;var c=this.low;return 32>a?b.fromBits(c<>>32-a,this.unsigned):b.fromBits(0,c<a?b.fromBits(this.low>>>a|c<<32-a,c>>a,this.unsigned):b.fromBits(c>>a-32,0<=c?0:-1,this.unsigned)};b.prototype.shiftRightUnsigned=function(a){a&=63;if(0==a)return this;var c=this.high;return 32>a?b.fromBits(this.low>>>a|c<<32-a,c>>>a,this.unsigned):32==a?b.fromBits(c,0,this.unsigned):b.fromBits(c>>>a-32,0,this.unsigned)};b.prototype.toSigned=function(){var a=this.clone();a.unsigned=p;return a};b.prototype.toUnsigned=function(){var a=this.clone();a.unsigned= +(function(r){function b(a,b,d){this.low=a|0;this.high=b|0;this.unsigned=!!d}var s={},t={};b.fromInt=function(a,c){var d;if(c){a>>>=0;if(0<=a&&256>a&&(d=t[a]))return d;d=new b(a,0>(a|0)?-1:0,!0);0<=a&&256>a&&(t[a]=d)}else{a|=0;if(-128<=a&&128>a&&(d=s[a]))return d;d=new b(a,0>a?-1:0,p);-128<=a&&128>a&&(s[a]=d)}return d};b.fromNumber=function(a,c){c=!!c;return isNaN(a)||!isFinite(a)?b.ZERO:!c&&a<=-u?b.MIN_SIGNED_VALUE:c&&0>=a?b.MIN_UNSIGNED_VALUE:!c&&a+1>=u?b.MAX_SIGNED_VALUE:c&&a>=v?b.MAX_UNSIGNED_VALUE:0> +a?b.fromNumber(-a,p).negate():new b(a%l|0,a/l|0,c)};b.fromBits=function(a,c,d){return new b(a,c,d)};b.from28Bits=function(a,c,d,e){return b.fromBits(a|c<<28,c>>>4|d<<24,e)};b.fromString=function(a,c,d){if(0==a.length)throw Error("number format error: empty string");if("NaN"===a||"Infinity"===a||"+Infinity"===a||"-Infinity"===a)return b.ZERO;"number"===typeof c&&(d=c,c=p);d=d||10;if(2>d||36f?(f=b.fromNumber(Math.pow(d,f)),e=e.multiply(f).add(b.fromNumber(k))):(e=e.multiply(c),e=e.add(b.fromNumber(k)))}return e};var l=4294967296,v=l*l,u=v/2,w=b.fromInt(16777216);b.ZERO=b.fromInt(0);b.ONE=b.fromInt(1);b.NEG_ONE=b.fromInt(-1);b.MAX_SIGNED_VALUE=b.fromBits(-1,2147483647,p); +b.MAX_UNSIGNED_VALUE=b.fromBits(-1,-1,!0);b.MAX_VALUE=b.MAX_SIGNED_VALUE;b.MIN_SIGNED_VALUE=b.fromBits(0,-2147483648,p);b.MIN_UNSIGNED_VALUE=b.fromBits(0,0,!0);b.MIN_VALUE=b.MIN_SIGNED_VALUE;b.prototype.toInt=function(){return this.unsigned?this.low>>>0:this.low};b.prototype.toNumber=function(){return this.unsigned?(this.high>>>0)*l+(this.low>>>0):this.high*l+(this.low>>>0)};b.prototype.toString=function(a){a=a||10;if(2>a||36f.length;)f="0"+f;e=""+f+e}};b.prototype.getHighBits=function(){return this.high};b.prototype.getHighBitsUnsigned=function(){return this.high>>>0}; +b.prototype.getLowBits=function(){return this.low};b.prototype.getLowBitsUnsigned=function(){return this.low>>>0};b.prototype.getNumBitsAbs=function(){if(this.isNegative())return this.equals(b.MIN_SIGNED_VALUE)?64:this.negate().getNumBitsAbs();for(var a=0!=this.high?this.high:this.low,c=31;0this.high};b.prototype.isOdd=function(){return 1== +(this.low&1)};b.prototype.equals=function(a){return this.unsigned!=a.unsigned&&this.high>>>31!=a.high>>>31?p:this.high==a.high&&this.low==a.low};b.prototype.notEquals=function(a){return!this.equals(a)};b.prototype.lessThan=function(a){return 0>this.compare(a)};b.prototype.lessThanOrEqual=function(a){return 0>=this.compare(a)};b.prototype.greaterThan=function(a){return 0>>0>this.high>>>0||a.high==this.high&&a.low>>>0>this.low>>>0?-1:1:this.subtract(a).isNegative()?-1:1};b.prototype.negate=function(){return!this.unsigned&&this.equals(b.MIN_SIGNED_VALUE)?b.MIN_SIGNED_VALUE:this.not().add(b.ONE)};b.prototype.add=function(a){var c=this.high>>>16,d=this.high&65535,e=this.low>>>16,g=a.high>>>16,f=a.high&65535,k=a.low>>>16,q;q=0+((this.low&65535)+(a.low&65535));a=0+(q>>>16);a+=e+k;e=0+ +(a>>>16);e+=d+f;d=0+(e>>>16);d=d+(c+g)&65535;return b.fromBits((a&65535)<<16|q&65535,d<<16|e&65535,this.unsigned)};b.prototype.subtract=function(a){return this.add(a.negate())};b.prototype.multiply=function(a){if(this.isZero()||a.isZero())return b.ZERO;if(this.equals(b.MIN_VALUE))return a.isOdd()?b.MIN_VALUE:b.ZERO;if(a.equals(b.MIN_VALUE))return this.isOdd()?b.MIN_VALUE:b.ZERO;if(this.isNegative())return a.isNegative()?this.negate().multiply(a.negate()):this.negate().multiply(a).negate();if(a.isNegative())return this.multiply(a.negate()).negate(); +if(this.lessThan(w)&&a.lessThan(w))return b.fromNumber(this.toNumber()*a.toNumber(),this.unsigned);var c=this.high>>>16,d=this.high&65535,e=this.low>>>16,g=this.low&65535,f=a.high>>>16,k=a.high&65535,q=a.low>>>16;a=a.low&65535;var n,h,m,l;l=0+g*a;m=0+(l>>>16);m+=e*a;h=0+(m>>>16);m=(m&65535)+g*q;h+=m>>>16;m&=65535;h+=d*a;n=0+(h>>>16);h=(h&65535)+e*q;n+=h>>>16;h&=65535;h+=g*k;n+=h>>>16;h&=65535;n=n+(c*a+d*q+e*k+g*f)&65535;return b.fromBits(m<<16|l&65535,n<<16|h,this.unsigned)};b.prototype.div=function(a){if(a.isZero())throw Error("division by zero"); +if(this.isZero())return b.ZERO;if(this.equals(b.MIN_SIGNED_VALUE)){if(a.equals(b.ONE)||a.equals(b.NEG_ONE))return min;if(a.equals(b.MIN_VALUE))return b.ONE;var c=this.shiftRight(1).div(a).shiftLeft(1);if(c.equals(b.ZERO))return a.isNegative()?b.ONE:b.NEG_ONE;var d=this.subtract(a.multiply(c));return c.add(d.div(a))}if(a.equals(b.MIN_VALUE))return b.ZERO;if(this.isNegative())return a.isNegative()?this.negate().div(a.negate()):this.negate().div(a).negate();if(a.isNegative())return this.div(a.negate()).negate(); +for(var e=b.ZERO,d=this;d.greaterThanOrEqual(a);){for(var c=Math.max(1,Math.floor(d.toNumber()/a.toNumber())),g=Math.ceil(Math.log(c)/Math.LN2),g=48>=g?1:Math.pow(2,g-48),f=b.fromNumber(c,this.unsigned),k=f.multiply(a);k.isNegative()||k.greaterThan(d);)c-=g,f=b.fromNumber(c,this.unsigned),k=f.multiply(a);f.isZero()&&(f=b.ONE);e=e.add(f);d=d.subtract(k)}return e};b.prototype.modulo=function(a){return this.subtract(this.div(a).multiply(a))};b.prototype.not=function(){return b.fromBits(~this.low,~this.high, +this.unsigned)};b.prototype.and=function(a){return b.fromBits(this.low&a.low,this.high&a.high,this.unsigned)};b.prototype.or=function(a){return b.fromBits(this.low|a.low,this.high|a.high,this.unsigned)};b.prototype.xor=function(a){return b.fromBits(this.low^a.low,this.high^a.high,this.unsigned)};b.prototype.shiftLeft=function(a){a&=63;if(0==a)return this;var c=this.low;return 32>a?b.fromBits(c<>>32-a,this.unsigned):b.fromBits(0,c<a?b.fromBits(this.low>>>a|c<<32-a,c>>a,this.unsigned):b.fromBits(c>>a-32,0<=c?0:-1,this.unsigned)};b.prototype.shiftRightUnsigned=function(a){a&=63;if(0==a)return this;var c=this.high;return 32>a?b.fromBits(this.low>>>a|c<<32-a,c>>>a,this.unsigned):32==a?b.fromBits(c,0,this.unsigned):b.fromBits(c>>>a-32,0,this.unsigned)};b.prototype.toSigned=function(){var a=this.clone();a.unsigned=p;return a};b.prototype.toUnsigned=function(){var a=this.clone();a.unsigned= !0;return a};b.prototype.clone=function(){return new b(this.low,this.high,this.unsigned)};"undefined"!=typeof module&&module.exports?module.exports=b:"undefined"!=typeof define&&define.amd?define("Math/Long",[],function(){return b}):(r.dcodeIO||(r.dcodeIO={}),r.dcodeIO.Long=b)})(this); diff --git a/README.md b/README.md index 5024925..2e48bd8 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Features * Shim compatible (include the script, then use var Long = dcodeIO.Long;) * [node.js](http://nodejs.org) compatible, also available via [npm](https://npmjs.org/package/long) * Fully documented using [jsdoc3](https://github.com/jsdoc3/jsdoc) +* API-compatible to the Closure Library implementation * Zero production dependencies * Small footprint diff --git a/docs/Long.html b/docs/Long.html index 5d9346f..effa27b 100644 --- a/docs/Long.html +++ b/docs/Long.html @@ -30,6 +30,8 @@

Long

+
A Long class for representing a 64-bit two's-complement integer value.
+
@@ -181,7 +183,7 @@
Parameters:
- Whether unsigned or not. Defaults to false (signed). + Whether unsigned or not. Defaults to `false` (signed). diff --git a/package.json b/package.json index 867af52..474f7b8 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "long", "version": "1.1.2", "author": "Daniel Wirtz ", - "description": "Long.js: A Long class for representing a 64-bit two's-complement integer value derived from the Closure Library extended with unsigned support.", + "description": "A Long class for representing a 64-bit two's-complement integer value.", "main": "Long.js", "repository": { "type": "git", diff --git a/tests/suite.js b/tests/suite.js index a0258fb..e71a512 100644 --- a/tests/suite.js +++ b/tests/suite.js @@ -128,6 +128,28 @@ var suite = { test.equal(longVal.toNumber(), -Long.MAX_SIGNED_VALUE); test.done(); } + }, + + // FIXME: There is no support for NaN or +/-Infinity + // "NaN": function(test) { + // test.ok(isNan(Long.fromNumber(NaN).toNumber()); + // test.strictEqual(Long.fromNumber(+Infinity).toNumber(), +Infinity); + // test.strictEqual(Long.fromNumber(-Infinity).toNumber(), -Infinity); + // test.done(); + // } + // One option could be to store NaN, Infinity etc. in high and set low to 0, while making sure to convert it in a + // proper way as soon as any math is called upon it. This might be as simple as that each of these values always + // "infects" other values, thus remaining untouched respectively changing the base long to its value. + // + // To clarify that, it all becomes zero atm, which usually is good enough but not perfect: + "NaN": function(test) { + test.strictEqual(Long.fromNumber(NaN), Long.ZERO); + test.strictEqual(Long.fromNumber(+Infinity), Long.ZERO); + test.strictEqual(Long.fromNumber(-Infinity), Long.ZERO); + test.strictEqual(Long.fromString(NaN+""), Long.ZERO); + test.strictEqual(Long.fromString(+Infinity+""), Long.ZERO); + test.strictEqual(Long.fromString(-Infinity+""), Long.ZERO); + test.done(); } };