diff options
author | Ben Noordhuis <info@bnoordhuis.nl> | 2017-12-20 01:15:22 +0100 |
---|---|---|
committer | Anna Henningsen <anna@addaleax.net> | 2017-12-27 19:47:16 +0100 |
commit | df30fd586d123fc189887fc0f9c7f27808374c2e (patch) | |
tree | 6823f22c8cefa8fb89cf6cb7c0416d0900b725ac /lib | |
parent | cb3de880beff3cbe5958ce07939d41884f743b17 (diff) | |
download | android-node-v8-df30fd586d123fc189887fc0f9c7f27808374c2e.tar.gz android-node-v8-df30fd586d123fc189887fc0f9c7f27808374c2e.tar.bz2 android-node-v8-df30fd586d123fc189887fc0f9c7f27808374c2e.zip |
buffer: optimize readDouble and readFloat methods
Compute the floating point number in JavaScript to avoid having to call
out to the C++ runtime. The improvements are not insubstantial:
improvement confidence p.value
value="big" endian="BE" type="Double" noAssert="false" 292.86 % *** 1.688367e-08
value="big" endian="BE" type="Double" noAssert="true" 353.19 % *** 6.079414e-10
value="big" endian="BE" type="Float" noAssert="false" 406.21 % *** 1.730122e-07
value="big" endian="BE" type="Float" noAssert="true" 450.81 % *** 6.909242e-07
value="big" endian="LE" type="Double" noAssert="false" 268.39 % *** 8.625486e-09
value="big" endian="LE" type="Double" noAssert="true" 310.66 % *** 2.798332e-15
value="big" endian="LE" type="Float" noAssert="false" 382.99 % *** 3.412057e-07
value="big" endian="LE" type="Float" noAssert="true" 394.60 % *** 1.406742e-07
value="inf" endian="BE" type="Double" noAssert="false" 312.91 % *** 7.407943e-12
value="inf" endian="BE" type="Double" noAssert="true" 392.47 % *** 3.821179e-08
value="inf" endian="BE" type="Float" noAssert="false" 466.01 % *** 8.953363e-08
value="inf" endian="BE" type="Float" noAssert="true" 460.76 % *** 5.381256e-09
value="inf" endian="LE" type="Double" noAssert="false" 279.50 % *** 2.390682e-09
value="inf" endian="LE" type="Double" noAssert="true" 335.30 % *** 3.587173e-09
value="inf" endian="LE" type="Float" noAssert="false" 439.77 % *** 1.057133e-07
value="inf" endian="LE" type="Float" noAssert="true" 426.72 % *** 4.353408e-09
value="nan" endian="BE" type="Double" noAssert="false" 271.18 % *** 2.281526e-05
value="nan" endian="BE" type="Double" noAssert="true" 312.63 % *** 1.974975e-07
value="nan" endian="BE" type="Float" noAssert="false" 429.17 % *** 2.416228e-07
value="nan" endian="BE" type="Float" noAssert="true" 461.39 % *** 1.956714e-08
value="nan" endian="LE" type="Double" noAssert="false" 267.03 % *** 9.938479e-12
value="nan" endian="LE" type="Double" noAssert="true" 276.93 % *** 7.842481e-08
value="nan" endian="LE" type="Float" noAssert="false" 415.97 % *** 8.082710e-07
value="nan" endian="LE" type="Float" noAssert="true" 433.68 % *** 1.030200e-07
value="small" endian="BE" type="Double" noAssert="false" 273.20 % *** 9.071652e-11
value="small" endian="BE" type="Double" noAssert="true" 326.25 % *** 3.120167e-08
value="small" endian="BE" type="Float" noAssert="false" 845.61 % *** 8.044170e-08
value="small" endian="BE" type="Float" noAssert="true" 868.61 % *** 2.944539e-08
value="small" endian="LE" type="Double" noAssert="false" 251.29 % *** 5.613930e-09
value="small" endian="LE" type="Double" noAssert="true" 286.82 % *** 8.149603e-10
value="small" endian="LE" type="Float" noAssert="false" 824.87 % *** 1.199729e-08
value="small" endian="LE" type="Float" noAssert="true" 834.35 % *** 4.799620e-08
value="zero" endian="BE" type="Double" noAssert="false" 216.70 % *** 3.872293e-12
value="zero" endian="BE" type="Double" noAssert="true" 239.31 % *** 6.439601e-09
value="zero" endian="BE" type="Float" noAssert="false" 353.75 % *** 3.639974e-07
value="zero" endian="BE" type="Float" noAssert="true" 388.86 % *** 7.074318e-10
value="zero" endian="LE" type="Double" noAssert="false" 179.34 % *** 5.230763e-06
value="zero" endian="LE" type="Double" noAssert="true" 199.66 % *** 2.177589e-11
value="zero" endian="LE" type="Float" noAssert="false" 299.55 % *** 9.961978e-08
value="zero" endian="LE" type="Float" noAssert="true" 333.30 % *** 2.470764e-08
PR-URL: https://github.com/nodejs/node/pull/17775
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/buffer.js | 89 |
1 files changed, 65 insertions, 24 deletions
diff --git a/lib/buffer.js b/lib/buffer.js index 40aaf37e09..180bfe6bad 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -31,10 +31,6 @@ const { indexOfBuffer, indexOfNumber, indexOfString, - readDoubleBE: _readDoubleBE, - readDoubleLE: _readDoubleLE, - readFloatBE: _readFloatBE, - readFloatLE: _readFloatLE, swap16: _swap16, swap32: _swap32, swap64: _swap64, @@ -1201,35 +1197,80 @@ Buffer.prototype.readInt32BE = function readInt32BE(offset, noAssert) { }; -Buffer.prototype.readFloatLE = function readFloatLE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 4, this.length); - return _readFloatLE(this, offset); +// For the casual reader who has not at the current time memorized the +// IEEE-754 standard in full detail: floating point numbers consist of +// a fraction, an exponent and a sign bit: 23+8+1 bits for single precision +// numbers and 52+11+1 bits for double precision numbers. +// +// A zero exponent is either a positive or negative zero, if the fraction +// is zero, or a denormalized number when it is non-zero. Multiplying the +// fraction by the smallest possible denormal yields the denormalized number. +// +// An all-bits-one exponent is either a positive or negative infinity, if +// the fraction is zero, or NaN when it is non-zero. The standard allows +// both quiet and signalling NaNs but since NaN is a canonical value in +// JavaScript, we cannot (and do not) distinguish between the two. +// +// Other exponents are regular numbers and are computed by subtracting the bias +// from the exponent (127 for single precision, 1023 for double precision), +// yielding an exponent in the ranges -126-127 and -1022-1024 respectively. +// +// Of interest is that the fraction of a normal number has an extra bit of +// precision that is not stored but is reconstructed by adding one after +// multiplying the fraction with the result of 2**-bits_in_fraction. + + +function toDouble(x0, x1) { + const frac = x0 + 0x100000000 * (x1 & 0xFFFFF); + const expt = (x1 >>> 20) & 2047; + const sign = (x1 >>> 31) ? -1 : 1; + if (expt === 0) { + if (frac === 0) return sign * 0; + return sign * frac * 2 ** -1074; + } else if (expt === 2047) { + if (frac === 0) return sign * Infinity; + return NaN; + } + return sign * 2 ** (expt - 1023) * (1 + frac * 2 ** -52); +} + + +function toFloat(x) { + const frac = x & 0x7FFFFF; + const expt = (x >>> 23) & 255; + const sign = (x >>> 31) ? -1 : 1; + if (expt === 0) { + if (frac === 0) return sign * 0; + return sign * frac * 2 ** -149; + } else if (expt === 255) { + if (frac === 0) return sign * Infinity; + return NaN; + } + return sign * 2 ** (expt - 127) * (1 + frac * 2 ** -23); +} + + +Buffer.prototype.readDoubleBE = function(offset, noAssert) { + const x1 = this.readUInt32BE(offset + 0, noAssert); + const x0 = this.readUInt32BE(offset + 4, noAssert); + return toDouble(x0, x1); }; -Buffer.prototype.readFloatBE = function readFloatBE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 4, this.length); - return _readFloatBE(this, offset); +Buffer.prototype.readDoubleLE = function(offset, noAssert) { + const x0 = this.readUInt32LE(offset + 0, noAssert); + const x1 = this.readUInt32LE(offset + 4, noAssert); + return toDouble(x0, x1); }; -Buffer.prototype.readDoubleLE = function readDoubleLE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 8, this.length); - return _readDoubleLE(this, offset); +Buffer.prototype.readFloatBE = function(offset, noAssert) { + return toFloat(this.readUInt32BE(offset, noAssert)); }; -Buffer.prototype.readDoubleBE = function readDoubleBE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) - checkOffset(offset, 8, this.length); - return _readDoubleBE(this, offset); +Buffer.prototype.readFloatLE = function(offset, noAssert) { + return toFloat(this.readUInt32LE(offset, noAssert)); }; |