bigfloat
Version:
Fast arbitrary precision math library for computational geometry.
434 lines (431 loc) • 15.5 kB
JavaScript
// 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()];