UNPKG

bigfloat

Version:

Fast arbitrary precision math library for computational geometry.

588 lines (587 loc) 22.7 kB
// 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();