emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js
author Martin Soch <Martin.Soch@oracle.com>
Thu, 07 Feb 2013 16:11:53 +0100
brancharithmetic
changeset 698 ff57af563cb8
parent 680 7ffb635a5c4f
child 700 b9bf26ea0118
permissions -rw-r--r--
Fix issue with Long.MIN_VALUE in Number.toExactString
Moved the first part of tests from vm/.../NumberTests to vmtest/.../LongArithmeticTest
     1 // empty line needed here
     2 Number.prototype.add32 = function(x) { return (this + x) | 0; };
     3 Number.prototype.sub32 = function(x) { return (this - x) | 0; };
     4 Number.prototype.mul32 = function(x) { 
     5     return (((this * (x >> 16)) << 16) + this * (x & 0xFFFF)) | 0;
     6 };
     7 
     8 Number.prototype.toInt8 = function()  { return (this << 24) >> 24; };
     9 Number.prototype.toInt16 = function() { return (this << 16) >> 16; };
    10 
    11 var __m32 = 0xFFFFFFFF;
    12 
    13 Number.prototype.next32 = function(low) {
    14   if (this === 0) {
    15     return low;
    16   }
    17   var l = new Number(low);
    18   l.hi = this | 0;
    19   return l;
    20 };
    21 
    22 Number.prototype.high32 = function() { 
    23     return this.hi ? this.hi : (Math.floor(this / (__m32+1))) | 0;
    24 };
    25 Number.prototype.toInt32 = function() { return this | 0; };
    26 Number.prototype.toFP = function() {
    27     return this.hi ? this.hi * (__m32+1) + this : this;
    28 };
    29 Number.prototype.toLong = function() {
    30     var hi = (this > __m32) ? (Math.floor(this / (__m32+1))) | 0 : 0;
    31     return hi.next32(Math.floor(this % (__m32+1)));
    32 };
    33 
    34 Number.prototype.toExactString = function() {
    35     if (this.hi) {
    36         // check for Long.MIN_VALUE
    37         if ((this.hi == (0x80000000 | 0)) && (this == 0)) {
    38             return '-9223372036854775808';
    39         }
    40         var res = 0;
    41         var a = [ 6,9,2,7,6,9,4,9,2,4 ];
    42         var s = '';
    43         var digit;
    44         var neg = this.hi < 0;
    45         if (neg) {
    46             var x = this.neg64();
    47             var hi = x.hi;
    48             var low = x;
    49         } else {
    50             var hi = this.hi;
    51             var low = this;
    52         }
    53         for (var i = 0; i < a.length; i++) {
    54             res += hi * a[i];
    55             var low_digit = low % 10;
    56             digit = (res % 10) + low_digit;
    57 
    58             low = Math.floor(low / 10);
    59             res = Math.floor(res / 10);
    60 
    61             if (digit >= 10) {
    62                 digit -= 10;
    63                 res++;
    64             }
    65             s = String(digit).concat(s);
    66         }
    67         s = String(res).concat(s).replace(/^0+/, '');
    68         return (neg ? '-' : '').concat(s);
    69     }
    70     return String(this);
    71 };
    72 
    73 Number.prototype.add64 = function(x) {
    74     var low = this + x;
    75     carry = 0;
    76     if (low > __m32) {
    77         carry = 1;
    78         low -= (__m32+1);
    79     }
    80     var hi = (this.high32() + x.high32() + carry) | 0;
    81     return hi.next32(low);
    82 };
    83 
    84 Number.prototype.sub64 = function(x) {
    85     var low = this - x;
    86     carry = 0;
    87     if (low < 0) {
    88         carry = 1;
    89         low += (__m32+1);
    90     }
    91     var hi = (this.high32() - x.high32() - carry) | 0;
    92     return hi.next32(low);
    93 };
    94 
    95 Number.prototype.mul64 = function(x) {
    96     var low = this.mul32(x);
    97     low += (low < 0) ? (__m32+1) : 0;
    98     // first count upper 32 bits of (this.low * x.low)
    99     var hi_hi = 0;
   100     var hi_low = 0;
   101     var m = 1;
   102     for (var i = 0; i < 32; i++) {
   103         if (x & m) {
   104             hi_hi += this >>> 16;
   105             hi_low += this & 0xFFFF
   106         }
   107         hi_low >>= 1;
   108         hi_low += (hi_hi & 1) ? 0x8000 : 0;
   109         hi_hi >>= 1;
   110         m <<= 1;
   111     }
   112     var hi = (hi_hi << 16) + hi_low;
   113     
   114     var m1 = this.high32().mul32(x);
   115     var m2 = this.mul32(x.high32());
   116     hi = hi.add32(m1).add32(m2);
   117     
   118     return hi.next32(low);
   119 };
   120 
   121 Number.prototype.and64 = function(x) {
   122     var low = this & x;
   123     low += (low < 0) ? (__m32+1) : 0;
   124     if (this.hi && x.hi) {
   125         var hi = this.hi & x.hi;
   126         return hi.next32(low);
   127     };
   128     return low;
   129 };
   130 
   131 Number.prototype.or64 = function(x) {
   132     var low = this | x;
   133     low += (low < 0) ? (__m32+1) : 0;
   134     if (this.hi || x.hi) {
   135         var hi = this.hi | x.hi;
   136         return hi.next32(low);
   137     };
   138     return low;
   139 };
   140 
   141 Number.prototype.xor64 = function(x) {
   142     var low = this ^ x;
   143     low += (low < 0) ? (__m32+1) : 0;
   144     if (this.hi || x.hi) {
   145         var hi = this.hi ^ x.hi;
   146         return hi.next32(low);
   147     };
   148     return low;
   149 };
   150 
   151 Number.prototype.shl64 = function(x) {
   152     if (x >= 32) {
   153         var hi = this << (x - 32);
   154         return hi.next32(0);
   155     } else {
   156         var hi = this.high32() << x;
   157         var low_reminder = this >> (32 - x);
   158         hi |= low_reminder;
   159         var low = this << x;
   160         low += (low < 0) ? (__m32+1) : 0;
   161         return hi.next32(low);
   162     }
   163 };
   164 
   165 Number.prototype.shr64 = function(x) {
   166     if (x >= 32) {
   167         var low = this.high32() >> (x - 32);
   168         low += (low < 0) ? (__m32+1) : 0;
   169         return low;
   170     } else {
   171         var low = this >> x;
   172         var hi_reminder = this.high32() << (32 - x);
   173         low |= hi_reminder;
   174         low += (low < 0) ? (__m32+1) : 0;
   175         var hi = this.high32() >> x;
   176         return hi.next32(low);
   177     }
   178 };
   179 
   180 Number.prototype.ushr64 = function(x) {
   181     if (x >= 32) {
   182         var low = this.high32() >>> (x - 32);
   183         low += (low < 0) ? (__m32+1) : 0;
   184         return low;
   185     } else {
   186         var low = this >>> x;
   187         var hi_reminder = this.high32() << (32 - x);
   188         low |= hi_reminder;
   189         low += (low < 0) ? (__m32+1) : 0;
   190         var hi = this.high32() >>> x;
   191         return hi.next32(low);
   192     }
   193 };
   194 
   195 Number.prototype.compare64 = function(x) {
   196     if (this.high32() === x.high32()) {
   197         return (this < x) ? -1 : ((this > x) ? 1 : 0);
   198     }
   199     return (this.high32() < x.high32()) ? -1 : 1;
   200 };
   201 
   202 Number.prototype.neg64 = function() {
   203     var hi = this.high32();
   204     var low = this;
   205     if ((hi === 0) && (low < 0)) { return -low; }
   206     hi = ~hi;
   207     low = ~low;
   208     low += (low < 0) ? (__m32+1) : 0;
   209     var ret = hi.next32(low);
   210     return ret.add64(1);
   211 };
   212 
   213 (function(numberPrototype) {
   214     function __Int64(hi32, lo32) {
   215         this.hi32 = hi32 | 0;
   216         this.lo32 = lo32 | 0;
   217 
   218         this.get32 = function(bitIndex) {
   219             var v0;
   220             var v1;
   221             bitIndex += 32;
   222             var selector = bitIndex >>> 5;
   223             switch (selector) {
   224                 case 0:
   225                     v0 = 0;
   226                     v1 = this.lo32;
   227                     break;
   228                 case 1:
   229                     v0 = this.lo32;
   230                     v1 = this.hi32;
   231                     break;
   232                 case 2:
   233                     v0 = this.hi32;
   234                     v1 = 0;
   235                     break
   236                 default:
   237                     return 0;
   238             }
   239 
   240             var shift = bitIndex & 31;
   241             if (shift === 0) {
   242                 return v0;
   243             }
   244 
   245             return (v1 << (32 - shift)) | (v0 >>> shift);
   246         }
   247 
   248         this.get16 = function(bitIndex) {
   249             return this.get32(bitIndex) & 0xffff;
   250         }
   251 
   252         this.set16 = function(bitIndex, value) {
   253             bitIndex += 32;
   254             var shift = bitIndex & 15;
   255             var svalue = (value & 0xffff) << shift; 
   256             var smask = 0xffff << shift;
   257             var selector = bitIndex >>> 4;
   258             switch (selector) {
   259                 case 0:
   260                     break;
   261                 case 1:
   262                     this.lo32 = (this.lo32 & ~(smask >>> 16))
   263                                     | (svalue >>> 16);
   264                     break;
   265                 case 2:
   266                     this.lo32 = (this.lo32 & ~smask) | svalue;
   267                     break;
   268                 case 3:
   269                     this.lo32 = (this.lo32 & ~(smask << 16))
   270                                     | (svalue << 16);
   271                     this.hi32 = (this.hi32 & ~(smask >>> 16))
   272                                     | (svalue >>> 16);
   273                     break;
   274                 case 4:
   275                     this.hi32 = (this.hi32 & ~smask) | svalue;
   276                     break;
   277                 case 5:
   278                     this.hi32 = (this.hi32 & ~(smask << 16))
   279                                     | (svalue << 16);
   280                     break;
   281             }
   282         }
   283 
   284         this.getDigit = function(index, shift) {
   285             return this.get16((index << 4) - shift);
   286         }
   287 
   288         this.getTwoDigits = function(index, shift) {
   289             return this.get32(((index - 1) << 4) - shift);
   290         }
   291 
   292         this.setDigit = function(index, shift, value) {
   293             this.set16((index << 4) - shift, value);
   294         }
   295 
   296         this.countSignificantDigits = function() {
   297             var sd;
   298             var remaining;
   299 
   300             if (this.hi32 === 0) {
   301                 if (this.lo32 === 0) {
   302                     return 0;
   303                 }
   304 
   305                 sd = 2;
   306                 remaining = this.lo32;
   307             } else {
   308                 sd = 4;
   309                 remaining = this.hi32;
   310             }
   311 
   312             if (remaining < 0) {
   313                 return sd;
   314             }
   315 
   316             return (remaining < 65536) ? sd - 1 : sd;
   317         }
   318         
   319         this.toNumber = function() {
   320             var lo32 = this.lo32;
   321             if (lo32 < 0) {
   322                 lo32 += 0x100000000;
   323             }
   324 
   325             return this.hi32.next32(lo32);
   326         }
   327     }
   328 
   329     function __countLeadingZeroes16(number) {
   330         var nlz = 0;
   331 
   332         if (number < 256) {
   333             nlz += 8;
   334             number <<= 8;
   335         }
   336 
   337         if (number < 4096) {
   338             nlz += 4;
   339             number <<= 4;
   340         }
   341 
   342         if (number < 16384) {
   343             nlz += 2;
   344             number <<= 2;
   345         }
   346 
   347         return (number < 32768) ? nlz + 1 : nlz;
   348     }
   349     
   350     // q = u / v; r = u - q * v;
   351     // v != 0
   352     function __div64(q, r, u, v) {
   353         var m = u.countSignificantDigits();
   354         var n = v.countSignificantDigits();
   355 
   356         q.hi32 = q.lo32 = 0;
   357 
   358         if (n === 1) {
   359             // v has single digit
   360             var vd = v.getDigit(0, 0);
   361             var carry = 0;
   362             for (var i = m - 1; i >= 0; --i) {
   363                 var ui = (carry << 16) | u.getDigit(i, 0);
   364                 if (ui < 0) {
   365                     ui += 0x100000000;
   366                 }
   367                 var qi = (ui / vd) | 0;
   368                 q.setDigit(i, 0, qi);
   369                 carry = ui - qi * vd;
   370             }
   371 
   372             r.hi32 = 0;
   373             r.lo32 = carry;
   374             return;
   375         }
   376 
   377         r.hi32 = u.hi32;  
   378         r.lo32 = u.lo32;
   379 
   380         if (m < n) {
   381             return;
   382         }
   383 
   384         // Normalize
   385         var nrm = __countLeadingZeroes16(v.getDigit(n - 1, 0));
   386 
   387         var vd1 = v.getDigit(n - 1, nrm);                
   388         var vd0 = v.getDigit(n - 2, nrm);
   389         for (var j = m - n; j >= 0; --j) {
   390             // Calculate qj estimate
   391             var ud21 = r.getTwoDigits(j + n, nrm);
   392             var ud2 = ud21 >>> 16;
   393             if (ud21 < 0) {
   394                 ud21 += 0x100000000;
   395             }
   396 
   397             var qest = (ud2 === vd1) ? 0xFFFF : ((ud21 / vd1) | 0);
   398             var rest = ud21 - qest * vd1;
   399 
   400             // 0 <= (qest - qj) <= 2
   401 
   402             // Refine qj estimate
   403             var ud0 = r.getDigit(j + n - 2, nrm);
   404             while ((qest * vd0) > ((rest * 0x10000) + ud0)) {
   405                 --qest;
   406                 rest += vd1;
   407             }
   408 
   409             // 0 <= (qest - qj) <= 1
   410             
   411             // Multiply and subtract
   412             var carry = 0;
   413             for (var i = 0; i < n; ++i) {
   414                 var vi = qest * v.getDigit(i, nrm);
   415                 var ui = r.getDigit(i + j, nrm) - carry - (vi & 0xffff);
   416                 r.setDigit(i + j, nrm, ui);
   417                 carry = (vi >>> 16) - (ui >> 16);
   418             }
   419             var uj = ud2 - carry;
   420 
   421             if (uj < 0) {
   422                 // qest - qj = 1
   423 
   424                 // Add back
   425                 --qest;
   426                 var carry = 0;
   427                 for (var i = 0; i < n; ++i) {
   428                     var ui = r.getDigit(i + j, nrm) + v.getDigit(i, nrm)
   429                                  + carry;
   430                     r.setDigit(i + j, nrm, ui);
   431                     carry = ui >> 16;
   432                 }
   433                 uj += carry;
   434             }
   435 
   436             q.setDigit(j, 0, qest);
   437             r.setDigit(j + n, nrm, uj);
   438         }
   439     }
   440     
   441     numberPrototype.div64 = function(x) {
   442         var negateResult = false;
   443         var u, v;
   444         
   445         if ((this.high32() & 0x80000000) != 0) {
   446             u = this.neg64();
   447             negateResult = !negateResult;
   448         } else {
   449             u = this;        
   450         }
   451 
   452         if ((x.high32() & 0x80000000) != 0) {
   453             v = x.neg64();
   454             negateResult = !negateResult;
   455         } else {
   456             v = x;
   457         }
   458 
   459         if ((v === 0) && (v.high32() === 0)) {
   460             // TODO: throw
   461         }
   462 
   463         if (u.high32() === 0) {
   464             if (v.high32() === 0) {
   465                 var result = (u / v) | 0;
   466                 return negateResult ? result.neg64() : result; 
   467             }
   468 
   469             return 0;
   470         }
   471 
   472         var u64 = new __Int64(u.high32(), u);
   473         var v64 = new __Int64(v.high32(), v);
   474         var q64 = new __Int64(0, 0);
   475         var r64 = new __Int64(0, 0);
   476 
   477         __div64(q64, r64, u64, v64);
   478 
   479         var result = q64.toNumber();
   480         return negateResult ? result.neg64() : result; 
   481     }
   482 
   483     numberPrototype.mod64 = function(x) {
   484         var negateResult = false;
   485         var u, v;
   486         
   487         if ((this.high32() & 0x80000000) != 0) {
   488             u = this.neg64();
   489             negateResult = !negateResult;
   490         } else {
   491             u = this;        
   492         }
   493 
   494         if ((x.high32() & 0x80000000) != 0) {
   495             v = x.neg64();
   496         } else {
   497             v = x;
   498         }
   499 
   500         if ((v === 0) && (v.high32() === 0)) {
   501             // TODO: throw
   502         }
   503 
   504         if (u.high32() === 0) {
   505             var result = (v.high32() === 0) ? (u % v) : u;
   506             return negateResult ? result.neg64() : result; 
   507         }
   508 
   509         var u64 = new __Int64(u.high32(), u);
   510         var v64 = new __Int64(v.high32(), v);
   511         var q64 = new __Int64(0, 0);
   512         var r64 = new __Int64(0, 0);
   513 
   514         __div64(q64, r64, u64, v64);
   515 
   516         var result = r64.toNumber();
   517         return negateResult ? result.neg64() : result; 
   518     }
   519 })(Number.prototype);