UNPKG

bigfloat

Version:

Fast arbitrary precision math library for computational geometry.

434 lines (431 loc) 15.5 kB
// This file is part of bigfloat, copyright (c) 2018- BusFaster Ltd. // Released under the MIT license, see LICENSE. import { BigFloat32 } from './BigFloat32'; export var dekkerSplitter = (1 << 27) + 1; export var limbsPerDigit53 = Math.log(10) / (53 * Math.log(2)); /** See Shewchuk page 7. */ /* function fastTwoSum(a: number, b: number, sum: number[]) { const estimate = a + b; sum[0] = b - (estimate - a); sum[1] = estimate; return(sum); } */ /** Error-free addition of two floating point numbers. * See Shewchuk page 8. Note that output order is swapped! */ function twoSum(a, b, sum) { var estimate = a + b; var b2 = estimate - a; var a2 = estimate - b2; sum[0] = (a - a2) + (b - b2); sum[1] = estimate; return (sum); } /** Error-free product of two floating point numbers. * Store approximate result in global variable tempProduct. * See Shewchuk page 20. * * @return Rounding error. */ function twoProduct(a, b) { tempProduct = a * b; var a2 = a * dekkerSplitter; var aHi = a2 - (a2 - a); var aLo = a - aHi; var b2 = b * dekkerSplitter; var bHi = b2 - (b2 - b); var bLo = b - bHi; return (aLo * bLo - (tempProduct - aHi * bHi - aLo * bHi - aHi * bLo)); } /** Arbitrary precision floating point number. Based on a multiple-component * expansion format and error free transformations. * * Maximum exponent is the same as for plain JavaScript numbers, * least significant representable binary digit is 2^-1074. */ var BigFloat53 = /** @class */ (function () { /** @param value Initial value, a plain JavaScript floating point number * (IEEE 754 double precision). */ function BigFloat53(value, base) { /** List of components ordered by increasing exponent. */ this.limbList = []; if (value) this.setValue(value, base); } BigFloat53.prototype.clone = function () { return (new BigFloat53().setBig(this)); }; /** Set value to zero. * * @return This object, for chaining. */ BigFloat53.prototype.setZero = function () { this.len = 0; return (this); }; BigFloat53.prototype.setValue = function (other, base) { if (typeof (other) == 'number') { return (this.setNumber(other)); } if (other instanceof BigFloat53) { return (this.setBig(other)); } return (this.setString(other.toString(), base || 10)); }; BigFloat53.prototype.setBig = function (other) { var len = other.len; this.len = len; for (var pos = 0; pos < len; ++pos) { this.limbList[pos] = other.limbList[pos]; } return (this); }; /** Set value to a plain JavaScript floating point number * (IEEE 754 double precision). * * @param value New value. * @return This object, for chaining. */ BigFloat53.prototype.setNumber = function (value) { this.limbList[0] = value; this.len = value && 1; return (this); }; BigFloat53.prototype.setString = function (value, base) { temp32[0].setValue(value, base); this.len = temp32[0].getExpansion(this.limbList); this.normalize(); return (this); }; /** Set value to the sum of two JavaScript numbers. * * @param a Augend. * @param b Addend. * @return This object, for chaining. */ BigFloat53.prototype.setSum = function (a, b) { this.len = 2; twoSum(a, b, this.limbList); return (this); }; /** Set value to the product of two JavaScript numbers. * @param a Multiplicand. * @param b Multiplier. * @return This object, for chaining. */ BigFloat53.prototype.setProduct = function (a, b) { this.len = 2; this.limbList[0] = twoProduct(a, b); this.limbList[1] = tempProduct; return (this); }; /** See Compress from Shewchuk page 25. */ // TODO: Test. BigFloat53.prototype.normalize = function () { var limbList = this.limbList; var len = this.len; var limb; if (len) { var a = len - 1; var b = len - 1; var q = limbList[a]; var err = void 0; while (a) { limb = limbList[--a]; err = q; q += limb; err = limb - (q - err); limbList[b] = q; b -= err && 1; q = err || q; } limbList[b] = q; while (++b < len) { limb = limbList[b]; err = q; q += limb; err -= q - limb; limbList[a] = err; a += err && 1; } limbList[a] = q; this.len = a + (q && 1); } return (this); }; /** Multiply this arbitrary precision float by a number. * See Scale-Expansion from Shewchuk page 21. * * @param b Multiplier, a JavaScript floating point number. * @param product Arbitrary precision float to overwrite with result. * @return Modified product object. */ BigFloat53.prototype.mulSmall = function (b, product) { var limbList = this.limbList; var productLimbs = product.limbList; var count = this.len; var t1, t2, t3; var srcPos = 0, dstPos = 0; /** Write output limb and move to next, unless a zero was written. */ function writeLimb(limb) { productLimbs[dstPos] = limb; dstPos += limb && 1; } writeLimb(twoProduct(limbList[srcPos++], b)); var q = tempProduct; while (srcPos < count) { t1 = twoProduct(limbList[srcPos++], b); t2 = q + t1; t3 = t2 - q; writeLimb(q - (t2 - t3) + (t1 - t3)); q = tempProduct + t2; writeLimb(t2 - (q - tempProduct)); } productLimbs[dstPos] = q; product.len = dstPos + (q && 1); return (product); }; /** Multiply this by an arbitrary precision multiplier. * Pass all components of the multiplier to mulSmall and sum the products. * * @param multiplier Number or arbitrary precision float. * @param product Arbitrary precision float to overwrite with result. * @return Modified product object. */ BigFloat53.prototype.mulBig = function (multiplier, product) { var limbList = multiplier.limbList; var pos = multiplier.len; if (!pos) return (product.setZero()); --pos; this.mulSmall(limbList[pos], pos ? temp53[pos & 1] : product); while (pos) { --pos; this.mulSmall(limbList[pos], product).addBig(temp53[~pos & 1], 1, pos ? temp53[pos & 1] : product); } return (product); }; /** Multiply number or arbitrary precision float with this one * and store result in another BigFloat53. * * @param multiplier Number or arbitrary precision float. * @param product Arbitrary precision float to overwrite with result. * If omitted, a new one is allocated. * @return Modified product object. */ BigFloat53.prototype.mul = function (multiplier, product) { product = product || new BigFloat53(); if (typeof (multiplier) == 'number') { return (this.mulSmall(multiplier, product)); } if (product == this) throw (new Error('Cannot multiply in place')); return (this.mulBig(multiplier, product)); }; BigFloat53.prototype.isZero = function () { var limbList = this.limbList; var pos = this.len; while (pos--) { if (limbList[pos]) return (false); } return (true); }; BigFloat53.prototype.getSign = function () { var t = this.len; return (t && (t = this.limbList[t - 1]) && (t > 0 ? 1 : -1)); }; /** Return an arbitrary number with sign matching the result of this - other. */ // TODO: Test. BigFloat53.prototype.deltaFrom = function (other) { var t = this.len; var sign = this.getSign(); var diff = sign; if (typeof (other) != 'number') { t = other.len; diff -= t && (t = other.limbList[t - 1]) && (t > 0 ? 1 : -1); if (diff || !sign) return (diff); this.addBig(other, -1, temp53[0]); } else { diff -= other && (other > 0 ? 1 : -1); if (diff || !sign) return (diff); this.addSmall(-other, temp53[0]); } t = temp53[0].len; return (t && temp53[0].limbList[t - 1]); }; /** Add a number to this arbitrary precision float. * See Grow-Expansion from Shewchuk page 10. * * @param b JavaScript floating point number to add. * @param sum Arbitrary precision float to overwrite with result. * @return Modified sum object. */ BigFloat53.prototype.addSmall = function (b, sum) { var limbList = this.limbList; var sumLimbs = sum.limbList; var count = this.len; var estimate; var a, a2, b2, err; var srcPos = 0, dstPos = 0; while (srcPos < count) { a = limbList[srcPos++]; estimate = a + b; b2 = estimate - a; a -= estimate - b2; err = a + (b - b2); sumLimbs[dstPos] = err; dstPos += err && 1; b = estimate; } sumLimbs[dstPos] = b; sum.len = dstPos + (b && 1); return (sum); }; /** Add another arbitrary precision float (multiplied by sign) to this one. * See Fast-Expansion-Sum from Shewchuk page 13. * * @param sign Multiplier for negating addend to implement subtraction. * @param sum Arbitrary precision float to overwrite with result. * @return Modified sum object. */ BigFloat53.prototype.addBig = function (addend, sign, sum) { var augendLimbs = this.limbList; var addendLimbs = addend.limbList; var sumLimbs = sum.limbList; var count = this.len + addend.len; var nextAugendPos = 0; var nextAddendPos = 0; var nextSumPos = 0; /** Latest limb of augend. */ var a = augendLimbs[nextAugendPos++]; /** Latest limb of addend. */ var b = addendLimbs[nextAddendPos++] * sign; /** Magnitude of latest augend limb. */ var a2 = a < 0 ? -a : a; /** Magnitude of latest addend limb. */ var b2 = b < 0 ? -b : b; var nextLimb, nextLimb2, prevLimb; var err; if (!count) return (sum.setZero()); // Append sentinel limbs to avoid testing for end of array. augendLimbs[this.len] = Infinity; addendLimbs[addend.len] = Infinity; /** Get next smallest limb from either augend or addend. * This avoids merging the two limb lists. */ function getNextLimb() { var result; if (a2 < b2) { result = a; a = augendLimbs[nextAugendPos++]; a2 = a < 0 ? -a : a; } else { result = b; b = addendLimbs[nextAddendPos++] * sign; b2 = b < 0 ? -b : b; } return (result); } var limb = getNextLimb(); while (--count) { nextLimb = getNextLimb(); prevLimb = limb; limb += nextLimb; nextLimb2 = limb - prevLimb; err = (prevLimb - (limb - nextLimb2)) + (nextLimb - nextLimb2); sumLimbs[nextSumPos] = err; nextSumPos += err && 1; } sumLimbs[nextSumPos] = limb; sum.len = nextSumPos + (limb && 1); return (sum); }; BigFloat53.prototype.addSub = function (addend, sign, result) { result = result || new BigFloat53(); if (typeof (addend) == 'number') return (this.addSmall(sign * addend, result)); return (this.addBig(addend, sign, result)); }; /** Add number or arbitrary precision float to this one * and store result in another BigFloat53. * * @param addend Number or arbitrary precision float. * @param sum Arbitrary precision float to overwrite with result. * If omitted, a new one is allocated. * @return Modified sum object. */ BigFloat53.prototype.add = function (addend, sum) { return (this.addSub(addend, 1, sum)); }; /** Subtract number or arbitrary precision float from this one * and store result in another BigFloat53. * * @param subtrahend Number or arbitrary precision float. * @param difference Arbitrary precision float to overwrite with result. * If omitted, a new one is allocated. * @return Modified difference object. */ BigFloat53.prototype.sub = function (subtrahend, difference) { return (this.addSub(subtrahend, -1, difference)); }; /** Round towards zero, to (at least) given number of base 2^53 fractional digits. */ BigFloat53.prototype.truncate = function (fractionLimbCount) { this.normalize(); var limbList = this.limbList; var len = this.len; // Use binary search to find last |limb| < 1. var lo = 0; var hi = len; var mid = 0; var limb = 0; while (lo < hi) { mid = (lo + hi) >> 1; limb = limbList[mid]; if (limb > -1 && limb < 1) { lo = mid + 1; } else { hi = mid; } } if (mid && (limb <= -1 || limb >= 1)) { limb = limbList[--mid]; } // Slice off limbs before and including it, // except the fractionLimbCount last ones. mid -= fractionLimbCount - 1; if (mid > 0) { this.len -= mid; len = this.len; for (var pos = 0; pos < len; ++pos) { limbList[pos] = limbList[pos + mid]; } } return (this); }; BigFloat53.prototype.round = function (decimalCount) { return (this.truncate(1 + ~~(decimalCount * limbsPerDigit53))); }; BigFloat53.prototype.valueOf = function () { var limbList = this.limbList; var len = this.len; var result = 0; for (var pos = 0; pos < len; ++pos) { result += limbList[pos]; } return (result); }; /** Convert to string in any even base supported by Number.toString. * @return String in lower case. */ BigFloat53.prototype.toString = function (base) { var limbList = this.limbList; var pos = this.len; temp32[pos & 1].setZero(); while (pos--) { temp32[~pos & 1].add(limbList[pos], temp32[pos & 1]); } return (temp32[~pos & 1].toString(base)); }; return BigFloat53; }()); export { BigFloat53 }; BigFloat53.prototype.cmp = BigFloat53.prototype.deltaFrom; /** Latest approximate product from twoProduct. */ var tempProduct = 0; /** Temporary values for internal calculations. */ var temp32 = [new BigFloat32(), new BigFloat32()]; /** Temporary values for internal calculations. */ var temp53 = [new BigFloat53(), new BigFloat53()];