UNPKG

18 kBJavaScriptView Raw
1"use strict";
2/** Difference between closest possible IEEE 754 doubles between 1 and 2. */
3exports.dblEpsilon = Math.pow(2, -52);
4/** Base for calculations, the bigger the better but must fit in 32 bits. */
5var limbSize = Math.pow(2, 32);
6/** Create a string with the given number of zero digits. */
7function zeroes(count) {
8 return (new Array(count + 1).join('0'));
9}
10var BigFloat = (function () {
11 function BigFloat(dbl) {
12 if (dbl)
13 this.setDouble(dbl);
14 else {
15 this.fractionLen = 0;
16 this.limbList = [];
17 }
18 }
19 /** Output EXACT value of an IEEE 754 double in base 2, 10 or 16.
20 * Exponent must be between -2 and 61, and last 3 bits of mantissa must be 0.
21 * Useful for debugging. */
22 BigFloat.doubleToString = function (dbl, base) {
23 if (base === void 0) { base = 10; }
24 var pad = BigFloat.padTbl[base];
25 var sign = '';
26 var out = '';
27 var limb;
28 var limbStr;
29 var groupSize = limbSize;
30 if (isNaN(dbl))
31 return ('NaN');
32 // For negative numbers, output sign and get absolute value.
33 if (dbl < 0) {
34 sign = '-';
35 dbl = -dbl;
36 }
37 if (!isFinite(dbl))
38 return (sign + 'Inf');
39 if (dbl < 1) {
40 out += '0';
41 }
42 else {
43 var iPart = Math.floor(dbl);
44 dbl -= iPart;
45 if (base == 10)
46 groupSize = 1000000000;
47 while (iPart) {
48 // Extract groups of digits starting from the least significant.
49 limb = iPart % groupSize;
50 iPart = (iPart - limb) / groupSize;
51 limbStr = limb.toString(base);
52 // Prepend digits to result.
53 out = limbStr + out;
54 // If more limbs remain, pad with zeroes to group length.
55 if (iPart)
56 out = pad.substr(limbStr.length) + out;
57 }
58 }
59 // Is there a fractional part remaining?
60 if (dbl > 0) {
61 out += '.';
62 if (base == 10) {
63 groupSize = 10;
64 pad = '';
65 }
66 while (dbl) {
67 // Extract groups of digits starting from the most significant.
68 dbl *= groupSize;
69 limb = dbl >>> 0;
70 dbl -= limb;
71 limbStr = limb.toString(base);
72 // Append digits to result and pad with zeroes to group length.
73 out += pad.substr(limbStr.length) + limbStr;
74 }
75 }
76 // Remove trailing zeroes.
77 return (sign + out.replace(/(\.[0-9a-z]*[1-9a-z])0+$/, '$1'));
78 };
79 /** Set value from a floating point number (probably IEEE 754 double). */
80 BigFloat.prototype.setDouble = function (dbl) {
81 if (dbl < 0) {
82 dbl = -dbl;
83 this.isNegative = 1;
84 }
85 else
86 this.isNegative = 0;
87 var iPart = Math.floor(dbl);
88 var fPart = dbl - iPart;
89 var fractionLen = 0;
90 var limbList = [];
91 var limb;
92 // Handle fractional part.
93 while (fPart) {
94 // Extract limbs starting from the most significant.
95 fPart *= limbSize;
96 limb = fPart >>> 0;
97 fPart -= limb;
98 // Append limb to value (limbs are reversed later).
99 limbList.push(limb);
100 ++fractionLen;
101 }
102 limbList.reverse();
103 // Handle integer part.
104 while (iPart) {
105 // Extract limbs starting from the least significant.
106 limb = iPart % limbSize; // Could also be iPart >>> 0
107 iPart = (iPart - limb) / limbSize;
108 // Append limb to value.
109 limbList.push(limb);
110 }
111 this.limbList = limbList;
112 this.fractionLen = fractionLen;
113 return (this);
114 };
115 /** Multiply by an integer and write output limbs to another list. */
116 BigFloat.prototype.mulInt = function (factor, dstLimbList, srcPos, dstPos, overwriteMask) {
117 if (!factor)
118 return (0);
119 var limbList = this.limbList;
120 var limbCount = limbList.length;
121 var limb;
122 var lo;
123 var carry = 0;
124 // limbList is an array of 32-bit ints but split here into 16-bit low
125 // and high words for multiplying by a 32-bit term, so the intermediate
126 // 48-bit multiplication results fit into 53 bits of IEEE 754 mantissa.
127 while (srcPos < limbCount) {
128 limb = limbList[srcPos++];
129 // Multiply lower half of limb with factor, making carry temporarily take 48 bits.
130 carry += factor * (limb & 0xffff);
131 // Get lowest 16 bits of full product.
132 lo = carry & 0xffff;
133 // Right shift by dividing because >> and >>> truncate to 32 bits before shifting.
134 carry = (carry - lo) / 65536;
135 // Multiply higher half of limb and combine with lowest 16 bits of full product.
136 carry += factor * (limb >>> 16);
137 lo |= carry << 16;
138 // Lowest 32 bits of full product are added to output limb.
139 limb = ((dstLimbList[dstPos] & overwriteMask) + lo) >>> 0;
140 dstLimbList[dstPos++] = limb;
141 // Highest 32 bits of full product stay in carry, also increment by 1 if previous sum overflowed.
142 carry = (carry / 65536) >>> 0;
143 // Bit twiddle equivalent to: if(limb < (lo >>> 0)) ++carry;
144 carry += (lo ^ (((limb - lo) ^ lo) & ~(limb ^ lo))) >>> 31;
145 }
146 // Extend result by one more limb if it overflows.
147 if (carry)
148 dstLimbList[dstPos] = carry;
149 return (carry);
150 };
151 BigFloat.prototype.mulBig = function (multiplier) {
152 var multiplierLimbs = multiplier.limbList;
153 var lenMultiplier = multiplierLimbs.length;
154 var product = new BigFloat();
155 var productLimbs = product.limbList;
156 for (var posMultiplier = 0; posMultiplier < lenMultiplier; ++posMultiplier) {
157 this.mulInt(multiplierLimbs[posMultiplier], productLimbs, 0, posMultiplier, 0xffffffff);
158 }
159 product.isNegative = this.isNegative ^ multiplier.isNegative;
160 product.fractionLen = this.fractionLen + multiplier.fractionLen;
161 return (product);
162 };
163 /** Multiply and return product in a new BigFloat. */
164 BigFloat.prototype.mul = function (multiplier) {
165 if (typeof (multiplier) == 'number') {
166 multiplier = BigFloat.tempFloat.setDouble(multiplier);
167 }
168 return (this.mulBig(multiplier));
169 };
170 BigFloat.prototype.absDeltaFrom = function (other) {
171 var limbList = this.limbList;
172 var otherList = other.limbList;
173 var limbCount = limbList.length;
174 var otherCount = otherList.length;
175 // Compare lengths.
176 var d = (limbCount - this.fractionLen) - (otherCount - other.fractionLen);
177 // If lengths are equal, compare each limb from most to least significant.
178 while (!d && limbCount && otherCount)
179 d = limbList[--limbCount] - otherList[--otherCount];
180 if (d)
181 return (d);
182 if (limbCount) {
183 do
184 d = limbList[--limbCount];
185 while (!d && limbCount);
186 }
187 else if (otherCount) {
188 do
189 d = -otherList[--otherCount];
190 while (!d && otherCount);
191 }
192 return (d);
193 };
194 BigFloat.prototype.isZero = function () {
195 var limbList = this.limbList;
196 var limbCount = limbList.length;
197 var d;
198 if (!limbCount)
199 return (true);
200 do
201 d = limbList[--limbCount];
202 while (!d && limbCount);
203 return (!d);
204 };
205 /** Return an arbitrary number with sign matching the result of this - other. */
206 BigFloat.prototype.deltaFrom = function (other) {
207 var isNegative = this.isNegative;
208 var d = other.isNegative - isNegative;
209 // Check if signs are different.
210 if (d) {
211 // Make sure positive and negative zero have no difference.
212 if (this.isZero() && other.isZero())
213 return (0);
214 // Return difference of signs.
215 return (d);
216 }
217 if (isNegative) {
218 return (-this.absDeltaFrom(other));
219 }
220 else {
221 return (this.absDeltaFrom(other));
222 }
223 };
224 BigFloat.prototype.addBig = function (addend) {
225 var augend = this;
226 var sum = new BigFloat();
227 var fractionLen = augend.fractionLen;
228 var len = fractionLen - addend.fractionLen;
229 if (len < 0) {
230 len = -len;
231 fractionLen += len;
232 augend = addend;
233 addend = this;
234 }
235 sum.isNegative = this.isNegative;
236 sum.fractionLen = fractionLen;
237 var sumLimbs = sum.limbList;
238 var augendLimbs = augend.limbList;
239 var addendLimbs = addend.limbList;
240 var posAugend = 0;
241 var posAddend = 0;
242 var carry = 0;
243 var limbSum;
244 // If one input has more fractional limbs, just copy the leftovers to output.
245 while (posAugend < len) {
246 sumLimbs[posAugend] = augendLimbs[posAugend];
247 ++posAugend;
248 }
249 var lenAddend = addendLimbs.length;
250 len = augendLimbs.length - posAugend;
251 if (len > lenAddend)
252 len = lenAddend;
253 // Calculate sum where input numbers overlap.
254 while (posAddend < len) {
255 carry += augendLimbs[posAugend] + addendLimbs[posAddend++];
256 limbSum = carry >>> 0;
257 carry = carry - limbSum && 1;
258 sumLimbs[posAugend++] = limbSum;
259 }
260 var posSum = posAugend;
261 if (len < lenAddend) {
262 len = lenAddend;
263 augend = addend;
264 posAugend = posAddend;
265 augendLimbs = addendLimbs;
266 }
267 else
268 len = augendLimbs.length;
269 // Copy leftover most significant limbs to output, propagating carry.
270 while (posAugend < len) {
271 carry += augendLimbs[posAugend++];
272 limbSum = carry >>> 0;
273 carry = carry - limbSum && 1;
274 sumLimbs[posSum++] = limbSum;
275 }
276 if (carry)
277 sumLimbs[posSum] = carry;
278 return (sum);
279 };
280 BigFloat.prototype.subBig = function (subtrahend) {
281 var minuend = this;
282 var difference = new BigFloat();
283 difference.isNegative = this.isNegative;
284 // Make sure the subtrahend is the smaller number.
285 if (minuend.absDeltaFrom(subtrahend) < 0) {
286 minuend = subtrahend;
287 subtrahend = this;
288 difference.isNegative ^= 1;
289 }
290 var fractionLen = minuend.fractionLen;
291 var len = fractionLen - subtrahend.fractionLen;
292 var differenceLimbs = difference.limbList;
293 var minuendLimbs = minuend.limbList;
294 var subtrahendLimbs = subtrahend.limbList;
295 var lenMinuend = minuendLimbs.length;
296 var lenSubtrahend = subtrahendLimbs.length;
297 var lenFinal = lenMinuend;
298 var posMinuend = 0;
299 var posSubtrahend = 0;
300 var posDifference = 0;
301 var carry = 0;
302 var limbDiff;
303 if (len >= 0) {
304 while (posMinuend < len) {
305 differenceLimbs[posMinuend] = minuendLimbs[posMinuend];
306 ++posMinuend;
307 }
308 len += lenSubtrahend;
309 if (len > lenMinuend)
310 len = lenMinuend;
311 posDifference = posMinuend;
312 }
313 else {
314 len = -len;
315 fractionLen += len;
316 lenFinal += len;
317 while (posSubtrahend < len) {
318 carry -= subtrahendLimbs[posSubtrahend];
319 limbDiff = carry >>> 0;
320 carry = -(carry < 0);
321 differenceLimbs[posSubtrahend++] = limbDiff;
322 }
323 len += lenMinuend;
324 if (len > lenSubtrahend)
325 len = lenSubtrahend;
326 posDifference = posSubtrahend;
327 }
328 difference.fractionLen = fractionLen;
329 // Calculate difference where input numbers overlap.
330 while (posDifference < len) {
331 carry += minuendLimbs[posMinuend++] - subtrahendLimbs[posSubtrahend++];
332 limbDiff = carry >>> 0;
333 carry = -(carry < 0);
334 differenceLimbs[posDifference++] = limbDiff;
335 }
336 // Copy leftover most significant limbs to output, propagating carry.
337 while (posDifference < lenFinal) {
338 carry += minuendLimbs[posMinuend++];
339 limbDiff = carry >>> 0;
340 carry = -(carry < 0);
341 differenceLimbs[posDifference++] = limbDiff;
342 }
343 return (difference);
344 };
345 BigFloat.prototype.addSub = function (addend, flip) {
346 if (typeof (addend) == 'number') {
347 addend = BigFloat.tempFloat.setDouble(addend);
348 }
349 if (this.isNegative ^ addend.isNegative ^ flip) {
350 return (this.subBig(addend));
351 }
352 else {
353 return (this.addBig(addend));
354 }
355 };
356 /** Add and return sum in a new BigFloat. */
357 BigFloat.prototype.add = function (addend) {
358 return (this.addSub(addend, 0));
359 };
360 /** Subtract and return difference in a new BigFloat. */
361 BigFloat.prototype.sub = function (subtrahend) {
362 return (this.addSub(subtrahend, 1));
363 };
364 /** Divide by integer, replacing current value by quotient. Return integer remainder. */
365 BigFloat.prototype.divInt = function (divisor) {
366 var limbList = this.limbList;
367 var limbNum = limbList.length;
368 var limb;
369 var hi, lo;
370 var carry = 0;
371 // If most significant limb is zero after dividing, decrement number of limbs remaining.
372 if (limbList[limbNum - 1] < divisor) {
373 carry = limbList[--limbNum];
374 limbList.length = limbNum;
375 }
376 while (limbNum--) {
377 limb = limbList[limbNum];
378 carry = carry * 0x10000 + (limb >>> 16);
379 hi = (carry / divisor) >>> 0;
380 carry = carry - hi * divisor;
381 carry = carry * 0x10000 + (limb & 0xffff);
382 lo = (carry / divisor) >>> 0;
383 carry = carry - lo * divisor;
384 limbList[limbNum] = ((hi << 16) | lo) >>> 0;
385 }
386 return (carry);
387 };
388 BigFloat.prototype.fractionToString = function (base, groupSize, digitList) {
389 var pad = BigFloat.padTbl[base];
390 var limbList = this.limbList;
391 var limbCount = this.fractionLen;
392 var limbNum = 0;
393 var limbStr;
394 // Skip least significant limbs that equal zero.
395 while (1) {
396 if (limbNum >= limbCount)
397 return;
398 if (limbList[limbNum])
399 break;
400 ++limbNum;
401 }
402 digitList.push('.');
403 var fPart = BigFloat.tempFloat;
404 fPart.limbList = limbList.slice(limbNum, limbCount);
405 limbCount -= limbNum;
406 limbNum = 0;
407 while (1) {
408 if (fPart.limbList[limbNum]) {
409 var carry = fPart.mulInt(groupSize, fPart.limbList, limbNum, limbNum, 0);
410 if (carry)
411 fPart.limbList.pop();
412 limbStr = '' + carry;
413 digitList.push(pad.substr(limbStr.length) + limbStr);
414 }
415 else if (++limbNum >= limbCount)
416 break;
417 }
418 };
419 /** Convert to string in base 2, 10 or 16. */
420 BigFloat.prototype.toString = function (base) {
421 if (base === void 0) { base = 10; }
422 var pad = BigFloat.padTbl[base];
423 var digitList = [];
424 var limbList = this.limbList;
425 var limb;
426 var limbStr;
427 if (base == 10) {
428 var groupSize = 1000000000;
429 var iPart = BigFloat.tempFloat;
430 iPart.limbList = limbList.slice(this.fractionLen);
431 // Loop while 2 or more limbs remain, requiring arbitrary precision division to extract digits.
432 while (iPart.limbList.length) {
433 limbStr = '' + iPart.divInt(groupSize);
434 // Prepend digits into final result, padded with zeroes to 9 digits.
435 // Since more limbs still remain, whole result will not have extra padding.
436 digitList.push(pad.substr(limbStr.length) + limbStr);
437 }
438 // Prepend last remaining limb and sign to result.
439 digitList.push('' + (iPart.limbList[0] || 0));
440 if (this.isNegative)
441 digitList.push('-');
442 digitList.reverse();
443 // Handle fractional part.
444 this.fractionToString(base, groupSize, digitList);
445 }
446 else {
447 var limbNum = limbList.length;
448 var fractionPos = this.fractionLen;
449 if (this.isNegative)
450 digitList.push('-');
451 if (limbNum == fractionPos)
452 digitList.push('0');
453 while (limbNum--) {
454 limbStr = limbList[limbNum].toString(base);
455 if (limbNum == fractionPos - 1)
456 digitList.push('.');
457 digitList.push(pad.substr(limbStr.length) + limbStr);
458 }
459 }
460 // Remove leading and trailing zeroes.
461 return (BigFloat.trim(digitList.join('')));
462 };
463 /** Remove leading and trailing insignificant zero digits. */
464 BigFloat.trim = function (str) {
465 return (str
466 .replace(/^(-?)0+([1-9a-z]|0(\.|$))/, '$1$2')
467 .replace(/(\.|(\.[0-9a-z]*[1-9a-z]))0+$/, '$2'));
468 };
469 BigFloat.padTbl = {
470 2: zeroes(32),
471 10: zeroes(9),
472 16: zeroes(8)
473 };
474 BigFloat.tempFloat = new BigFloat();
475 return BigFloat;
476})();
477exports.BigFloat = BigFloat;