bigfloat
Version:
Fast arbitrary precision math library for computational geometry.
588 lines (587 loc) • 22.7 kB
JavaScript
// This file is part of bigfloat, copyright (c) 2015- BusFaster Ltd.
// Released under the MIT license, see LICENSE.
import { BaseInfo32, limbSize32, limbInv32, limbsPerDigit32 } from './BaseInfo32';
import { trimNumber } from './util';
var BigFloat32 = /** @class */ (function () {
function BigFloat32(value, base) {
/** List of digits in base 2^32, least significant first. */
this.limbList = [];
value ? this.setValue(value, base) : this.setZero();
}
BigFloat32.prototype.clone = function () {
return (new BigFloat32().setBig(this));
};
BigFloat32.prototype.setZero = function () {
this.sign = 1;
this.fractionLen = 0;
this.len = 0;
return (this);
};
BigFloat32.prototype.setValue = function (other, base) {
if (typeof (other) == 'number') {
return (this.setNumber(other));
}
if (other instanceof BigFloat32) {
return (this.setBig(other));
}
return (this.setString(other.toString(), base || 10));
};
BigFloat32.prototype.setBig = function (other) {
var len = other.len;
this.sign = other.sign;
this.fractionLen = other.fractionLen;
this.len = len;
for (var pos = 0; pos < len; ++pos) {
this.limbList[pos] = other.limbList[pos];
}
return (this);
};
/** Set value from a floating point number (probably IEEE 754 double). */
BigFloat32.prototype.setNumber = function (value) {
if (value < 0) {
value = -value;
this.sign = -1;
}
else {
this.sign = 1;
}
var iPart = Math.floor(value);
var fPart = value - iPart;
var fractionLen = 0;
var limbList = this.limbList;
var limb;
var len = 0;
// Handle fractional part.
while (fPart) {
// Extract limbs starting from the most significant.
fPart *= limbSize32;
limb = fPart >>> 0;
fPart -= limb;
// Append limb to value (limbs are reversed later).
limbList[len++] = limb;
++fractionLen;
}
// Reverse array from 0 to len.
var pos = 0;
while (--len > pos) {
limb = limbList[pos];
limbList[pos++] = limbList[len];
limbList[len] = limb;
}
len += pos + 1;
// Handle integer part.
while (iPart) {
// Extract limbs starting from the least significant.
limb = iPart % limbSize32; // Could also be iPart >>> 0
iPart = (iPart - limb) / limbSize32;
// Append limb to value.
limbList[len++] = limb;
}
this.limbList = limbList;
this.fractionLen = fractionLen;
this.len = len;
return (this);
};
BigFloat32.prototype.parseFraction = function (value, base, start, offset, limbBase, limbDigits) {
var limbList = this.limbList;
var pos = value.length;
// Set limbs to zero, because divInt uses them as input.
var limbNum = offset - 1;
while (limbNum) {
limbList[--limbNum] = 0;
}
// Read initial digits so their count becomes divisible by limbDigits.
var posNext = pos - ((pos - start + limbDigits - 1) % limbDigits + 1);
limbList[offset - 1] = parseInt(value.substr(posNext, pos - posNext), base);
this.divInt(Math.pow(base, pos - posNext), offset);
pos = posNext;
// Read rest of the digits in limbDigits sized chunks.
while (pos > start) {
pos -= limbDigits;
limbList[offset - 1] = parseInt(value.substr(pos, limbDigits), base);
// Divide by maximum power of base that fits in a limb.
this.divInt(limbBase, offset);
}
};
BigFloat32.prototype.setString = function (value, base) {
var _a = BaseInfo32.init(base), limbBase = _a.limbBase, limbDigits = _a.limbDigits, limbDigitsExact = _a.limbDigitsExact;
var limbList = this.limbList;
var pos = -1;
var c;
this.sign = 1;
// Handle leading signs and zeroes.
while (1) {
c = value.charAt(++pos);
switch (c) {
case '-':
this.sign = -1;
case '+':
case '0':
continue;
}
break;
}
var posDot = (value.indexOf('.', pos) + 1 || value.length + 1) - 1;
// Handle fractional part.
if (posDot < value.length - 1) {
// Reserve enough limbs to contain digits in fractional part.
var len = ~~((value.length - posDot - 1) / limbDigitsExact) + 1;
this.parseFraction(value, base, posDot + 1, len + 1, limbBase, limbDigits);
this.fractionLen = len;
this.len = len;
// Remove trailing zeroes.
this.trimLeast();
}
else {
this.fractionLen = 0;
this.len = 0;
}
var offset = this.fractionLen;
// Handle integer part.
if (posDot > pos) {
// Read initial digits so their count becomes divisible by limbDigits.
var posNext = pos + (posDot - pos + limbDigits - 1) % limbDigits + 1;
++this.len;
limbList[offset] = parseInt(value.substr(pos, posNext - pos), base);
pos = posNext;
// Read rest of the digits in limbDigits sized chunks.
while (pos < posDot) {
// Multiply by maximum power of base that fits in a limb.
if (this.mulInt(limbBase, limbList, offset, offset, 0))
++this.len;
// Add latest limb.
limbList[offset] += parseInt(value.substr(pos, limbDigits), base);
pos += limbDigits;
}
}
return (this);
};
/** Trim zero limbs from most significant end. */
BigFloat32.prototype.trimMost = function () {
var limbList = this.limbList;
var fractionLen = this.fractionLen;
var len = this.len;
while (len > fractionLen && !limbList[len - 1])
--len;
this.len = len;
};
/** Trim zero limbs from least significant end. */
BigFloat32.prototype.trimLeast = function () {
var limbList = this.limbList;
var len = this.fractionLen;
var pos = 0;
while (pos < len && !limbList[pos])
++pos;
if (pos)
this.truncate(len - pos);
};
/** Multiply by an integer and write output limbs to another list. */
BigFloat32.prototype.mulInt = function (factor, dstLimbList, srcPos, dstPos, overwriteMask) {
if (!factor)
return (0);
var limbList = this.limbList;
var limbCount = this.len;
var limb;
var lo;
var carry = 0;
// limbList is an array of 32-bit ints but split here into 16-bit low
// and high words for multiplying by a 32-bit term, so the intermediate
// 48-bit multiplication results fit into 53 bits of IEEE 754 mantissa.
while (srcPos < limbCount) {
limb = limbList[srcPos++];
// Multiply lower half of limb with factor, making carry temporarily take 48 bits.
carry += factor * (limb & 0xffff);
// Get lowest 16 bits of full product.
lo = carry & 0xffff;
// Right shift by dividing because >> and >>> truncate to 32 bits before shifting.
carry = (carry - lo) / 65536;
// Multiply higher half of limb and combine with lowest 16 bits of full product.
carry += factor * (limb >>> 16);
lo |= carry << 16;
// Lowest 32 bits of full product are added to output limb.
limb = ((dstLimbList[dstPos] & overwriteMask) + lo) >>> 0;
dstLimbList[dstPos++] = limb;
// Highest 32 bits of full product stay in carry, also increment by 1 if previous sum overflowed.
carry = (carry / 65536) >>> 0;
// Bit twiddle equivalent to: if(limb < (lo >>> 0)) ++carry;
carry += (lo ^ (((limb - lo) ^ lo) & ~(limb ^ lo))) >>> 31;
}
// Extend result by one more limb if it overflows.
if (carry)
dstLimbList[dstPos] = carry;
return (carry);
};
BigFloat32.prototype.mulBig = function (multiplier, product) {
if (this.isZero() || multiplier.isZero())
return (product.setZero());
var multiplierLimbs = multiplier.limbList;
var lenMultiplier = multiplier.len;
var productLimbs = product.limbList;
var posProduct = this.len + lenMultiplier;
product.len = posProduct;
// TODO: Only clear from len to len + lenMultiplier
while (posProduct--) {
productLimbs[posProduct] = 0;
}
this.mulInt(multiplierLimbs[0], productLimbs, 0, 0, 0);
for (var posMultiplier = 1; posMultiplier < lenMultiplier; ++posMultiplier) {
this.mulInt(multiplierLimbs[posMultiplier], productLimbs, 0, posMultiplier, 0xffffffff);
}
product.sign = this.sign * multiplier.sign;
product.fractionLen = this.fractionLen + multiplier.fractionLen;
product.trimMost();
product.trimLeast();
return (product);
};
/** Multiply and return product in a new BigFloat32. */
BigFloat32.prototype.mul = function (multiplier, product) {
product = product || new BigFloat32();
if (typeof (multiplier) == 'number') {
multiplier = temp32.setNumber(multiplier);
}
if (product == this)
throw (new Error('Multiplication in place is unsupported'));
return (this.mulBig(multiplier, product));
};
BigFloat32.prototype.absDeltaFrom = function (other) {
if (typeof (other) == 'number') {
other = temp32.setNumber(other);
}
var limbList = this.limbList;
var otherList = other.limbList;
var limbCount = this.len;
var otherCount = other.len;
// Compare lengths.
// Note: leading zeroes in integer part must be trimmed for this to work!
var d = (limbCount - this.fractionLen) - (otherCount - other.fractionLen);
// If lengths are equal, compare each limb from most to least significant.
while (!d && limbCount && otherCount)
d = limbList[--limbCount] - otherList[--otherCount];
if (d)
return (d);
if (limbCount) {
do
d = limbList[--limbCount];
while (!d && limbCount);
}
else if (otherCount) {
do
d = -otherList[--otherCount];
while (!d && otherCount);
}
return (d);
};
BigFloat32.prototype.isZero = function () {
return (this.len == 0);
};
BigFloat32.prototype.getSign = function () {
return (this.len && this.sign);
};
/** Return an arbitrary number with sign matching the result of this - other. */
BigFloat32.prototype.deltaFrom = function (other) {
if (typeof (other) == 'number') {
other = temp32.setNumber(other);
}
return (
// Make positive and negative zero equal.
this.len + other.len && (
// Compare signs.
this.sign - other.sign ||
// Finally compare full values.
this.absDeltaFrom(other) * this.sign));
};
BigFloat32.prototype.addBig = function (addend, sum) {
var augend = this;
var fractionLen = augend.fractionLen;
var len = fractionLen - addend.fractionLen;
if (len < 0) {
len = -len;
fractionLen += len;
augend = addend;
addend = this;
}
sum.sign = this.sign;
sum.fractionLen = fractionLen;
var sumLimbs = sum.limbList;
var augendLimbs = augend.limbList;
var addendLimbs = addend.limbList;
var posAugend = 0;
var posAddend = 0;
var carry = 0;
var limbSum;
// If one input has more fractional limbs, just copy the leftovers to output.
while (posAugend < len) {
sumLimbs[posAugend] = augendLimbs[posAugend];
++posAugend;
}
var lenAddend = addend.len;
len = augend.len - posAugend;
if (len > lenAddend)
len = lenAddend;
// Calculate sum where input numbers overlap.
while (posAddend < len) {
carry += augendLimbs[posAugend] + addendLimbs[posAddend++];
limbSum = carry >>> 0;
carry = carry - limbSum && 1;
sumLimbs[posAugend++] = limbSum;
}
var posSum = posAugend;
if (len < lenAddend) {
len = lenAddend;
augend = addend;
posAugend = posAddend;
augendLimbs = addendLimbs;
}
else
len = augend.len;
// Copy leftover most significant limbs to output, propagating carry.
while (posAugend < len) {
carry += augendLimbs[posAugend++];
limbSum = carry >>> 0;
carry = carry - limbSum && 1;
sumLimbs[posSum++] = limbSum;
}
if (carry)
sumLimbs[posSum++] = carry;
sum.len = posSum;
sum.trimLeast();
return (sum);
};
BigFloat32.prototype.subBig = function (subtrahend, difference) {
var minuend = this;
difference.sign = this.sign;
// Make sure the subtrahend is the smaller number.
if (minuend.absDeltaFrom(subtrahend) < 0) {
minuend = subtrahend;
subtrahend = this;
difference.sign = -this.sign;
}
var fractionLen = minuend.fractionLen;
var len = fractionLen - subtrahend.fractionLen;
var differenceLimbs = difference.limbList;
var minuendLimbs = minuend.limbList;
var subtrahendLimbs = subtrahend.limbList;
var lenMinuend = minuend.len;
var lenSubtrahend = subtrahend.len;
var lenFinal = lenMinuend;
var posMinuend = 0;
var posSubtrahend = 0;
var posDifference = 0;
var carry = 0;
var limbDiff;
if (len >= 0) {
while (posMinuend < len) {
differenceLimbs[posMinuend] = minuendLimbs[posMinuend];
++posMinuend;
}
len += lenSubtrahend;
if (len > lenMinuend)
len = lenMinuend;
posDifference = posMinuend;
}
else {
len = -len;
fractionLen += len;
lenFinal += len;
while (posSubtrahend < len) {
carry -= subtrahendLimbs[posSubtrahend];
limbDiff = carry >>> 0;
carry = -(carry < 0);
differenceLimbs[posSubtrahend++] = limbDiff;
}
len += lenMinuend;
if (len > lenSubtrahend)
len = lenSubtrahend;
posDifference = posSubtrahend;
}
difference.fractionLen = fractionLen;
// Calculate difference where input numbers overlap.
while (posDifference < len) {
carry += minuendLimbs[posMinuend++] - subtrahendLimbs[posSubtrahend++];
limbDiff = carry >>> 0;
carry = -(carry < 0);
differenceLimbs[posDifference++] = limbDiff;
}
// Copy leftover most significant limbs to output, propagating carry.
while (posDifference < lenFinal) {
carry += minuendLimbs[posMinuend++];
limbDiff = carry >>> 0;
carry = -(carry < 0);
differenceLimbs[posDifference++] = limbDiff;
}
difference.len = posDifference;
difference.trimMost();
difference.trimLeast();
return (difference);
};
BigFloat32.prototype.addSub = function (addend, sign, result) {
result = result || new BigFloat32();
if (result == this)
throw (new Error('Addition and subtraction in place is unsupported'));
if (typeof (addend) == 'number') {
addend = temp32.setNumber(addend);
}
if (this.sign * addend.sign * sign < 0) {
return (this.subBig(addend, result));
}
else {
return (this.addBig(addend, result));
}
};
/** Add and return sum in a new BigFloat32. */
BigFloat32.prototype.add = function (addend, sum) {
return (this.addSub(addend, 1, sum));
};
/** Subtract and return difference in a new BigFloat32. */
BigFloat32.prototype.sub = function (subtrahend, difference) {
return (this.addSub(subtrahend, -1, difference));
};
/** Round towards zero, to given number of base 2^32 fractional digits. */
BigFloat32.prototype.truncate = function (fractionLimbCount) {
var diff = this.fractionLen - fractionLimbCount;
if (diff > 0) {
this.fractionLen = fractionLimbCount;
this.len -= diff;
var len = this.len;
var limbList = this.limbList;
for (var pos = 0; pos < len; ++pos) {
limbList[pos] = limbList[pos + diff];
}
}
return (this);
};
BigFloat32.prototype.round = function (decimalCount) {
return (this.truncate(1 + ~~(decimalCount * limbsPerDigit32)));
};
/** Divide by integer, replacing current value by quotient. Return integer remainder. */
BigFloat32.prototype.divInt = function (divisor, pos) {
var limbList = this.limbList;
var limb;
var hi, lo;
var carry = 0;
// If most significant limb is zero after dividing, decrement number of limbs remaining.
if (limbList[pos - 1] < divisor) {
carry = limbList[--pos];
this.len = pos;
}
while (pos--) {
limb = limbList[pos];
carry = carry * 0x10000 + (limb >>> 16);
hi = (carry / divisor) >>> 0;
carry = carry - hi * divisor;
carry = carry * 0x10000 + (limb & 0xffff);
lo = (carry / divisor) >>> 0;
carry = carry - lo * divisor;
limbList[pos] = ((hi << 16) | lo) >>> 0;
}
return (carry);
};
BigFloat32.prototype.fractionToString = function (base, digitList) {
var _a = BaseInfo32.init(base), pad = _a.pad, limbBase = _a.limbBase;
var limbList = this.limbList;
var limbCount = this.fractionLen;
var limbNum = 0;
var limbStr;
if (base & 1) {
throw (new Error('Conversion of floating point values to odd bases is unsupported'));
}
// Skip least significant limbs that equal zero.
while (limbNum < limbCount && !limbList[limbNum])
++limbNum;
if (limbNum >= limbCount)
return;
digitList.push('.');
var fPart = temp32;
fPart.limbList = limbList.slice(limbNum, limbCount);
fPart.len = limbCount - limbNum;
limbNum = 0;
while (limbNum < fPart.len) {
if (fPart.limbList[limbNum]) {
var carry = fPart.mulInt(limbBase, fPart.limbList, limbNum, limbNum, 0);
limbStr = carry.toString(base);
digitList.push(pad.substr(limbStr.length) + limbStr);
}
else
++limbNum;
}
};
BigFloat32.prototype.getExpansion = function (output) {
var limbList = this.limbList;
var len = this.len;
var exp = this.sign;
var pos = this.fractionLen;
while (pos--) {
exp *= limbInv32;
}
while (++pos < len) {
output[pos] = limbList[pos] * exp;
exp *= limbSize32;
}
return (len);
};
BigFloat32.prototype.valueOf = function () {
var limbList = this.limbList;
var result = 0;
var exp = limbInv32 * this.sign;
var len = this.fractionLen;
var pos = 0;
while (pos < len) {
result = result * limbInv32 + limbList[pos++];
}
len = this.len;
while (pos < len) {
result = result * limbInv32 + limbList[pos++];
exp *= limbSize32;
}
return (result * exp);
};
/** Convert to string in any even base supported by Number.toString.
* @return String in lower case. */
BigFloat32.prototype.toString = function (base) {
if (base === void 0) { base = 10; }
var _a = BaseInfo32.init(base), pad = _a.pad, limbBase = _a.limbBase;
var digitList = [];
var limbList = this.limbList;
var limb;
var limbStr;
if (limbBase != limbSize32) {
var iPart = temp32;
iPart.limbList = limbList.slice(this.fractionLen, this.len);
iPart.len = this.len - this.fractionLen;
// Loop while 2 or more limbs remain, requiring arbitrary precision division to extract digits.
while (iPart.len > 1) {
limbStr = iPart.divInt(limbBase, iPart.len).toString(base);
// Prepend digits into final result, padded with zeroes to 9 digits.
// Since more limbs still remain, whole result will not have extra padding.
digitList.push(pad.substr(limbStr.length) + limbStr);
}
// Prepend last remaining limb and sign to result.
digitList.push('' + (iPart.limbList[0] || 0));
if (this.sign < 0)
digitList.push('-');
digitList.reverse();
// Handle fractional part.
this.fractionToString(base, digitList);
}
else {
var limbNum = this.len;
var fractionPos = this.fractionLen;
if (this.sign < 0)
digitList.push('-');
if (limbNum == fractionPos)
digitList.push('0');
while (limbNum--) {
limbStr = limbList[limbNum].toString(base);
if (limbNum == fractionPos - 1)
digitList.push('.');
digitList.push(pad.substr(limbStr.length) + limbStr);
}
}
// Remove leading and trailing zeroes.
return (trimNumber(digitList.join('')));
};
return BigFloat32;
}());
export { BigFloat32 };
BigFloat32.prototype.cmp = BigFloat32.prototype.deltaFrom;
var temp32 = new BigFloat32();