Skip to content

Commit b667b70

Browse files
committed
Implement comparison, addition and subtraction.
1 parent aa8b60e commit b667b70

3 files changed

Lines changed: 254 additions & 4 deletions

File tree

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ bigfloat
33

44
[![npm version](https://img.shields.io/npm/v/bigfloat.svg)](https://www.npmjs.com/package/bigfloat)
55

6-
This library is under construction, the only features currently working are conversion from numbers and to strings.
7-
86
`bigfloat` is a fast arbitrary precision math library optimized for computational geometry and geoinformatics.
97
It provides base 2 floating point:
108

@@ -17,7 +15,7 @@ It provides base 2 floating point:
1715

1816
without ever losing any significant bits.
1917

20-
Internally numbers are represented in 32-bit limbs somewhat like in the [GMP](https://gmplib.org/) library.
18+
Internally numbers are represented in 32-bit limbs somewhat like in the [GMP](https://gmplib.org/manual/Float-Internals.html) library.
2119

2220
`bigfloat` is optimized for exponents relatively close to zero.
2321

src/bigfloat.ts

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,235 @@ export class BigFloat {
201201
return(this.mulBig(factor as BigFloat));
202202
}
203203

204+
absDeltaFrom(other: BigFloat) {
205+
let limbList = this.limbList;
206+
let otherList = other.limbList;
207+
let limbCount = limbList.length;
208+
let otherCount = otherList.length;
209+
210+
// Compare lengths.
211+
let d = (limbCount - this.fractionLen) - (otherCount - other.fractionLen);
212+
// If lengths are equal, compare each limb from most to least significant.
213+
while(!d && limbCount && otherCount) d = limbList[--limbCount] - otherList[--otherCount];
214+
215+
if(d) return(d);
216+
217+
if(limbCount) {
218+
do d = limbList[--limbCount]; while(!d && limbCount);
219+
} else if(otherCount) {
220+
do d = -otherList[--otherCount]; while(!d && otherCount);
221+
}
222+
223+
return(d);
224+
}
225+
226+
isZero() {
227+
let limbList = this.limbList;
228+
let limbCount = limbList.length;
229+
let d: number;
230+
231+
if(!limbCount) return(true);
232+
233+
do d = limbList[--limbCount]; while(!d && limbCount);
234+
235+
return(!d);
236+
}
237+
238+
/** Return an arbitrary number with sign matching the result of this - other. */
239+
240+
deltaFrom(other: BigFloat) {
241+
let isNegative = this.isNegative;
242+
let d = other.isNegative - isNegative;
243+
244+
// Check if signs are different.
245+
if(d) {
246+
// Make sure positive and negative zero have no difference.
247+
if(this.isZero() && other.isZero()) return(0);
248+
249+
// Return difference of signs.
250+
return(d);
251+
}
252+
253+
if(isNegative) {
254+
return(-this.absDeltaFrom(other));
255+
} else {
256+
return(this.absDeltaFrom(other));
257+
}
258+
}
259+
260+
addBig(addend: BigFloat) {
261+
let augend = this as BigFloat;
262+
let sum = new BigFloat();
263+
264+
let fractionLen = augend.fractionLen;
265+
let len = fractionLen - addend.fractionLen;
266+
267+
if(len < 0) {
268+
len = -len;
269+
fractionLen += len;
270+
augend = addend;
271+
addend = this;
272+
}
273+
274+
sum.isNegative = this.isNegative;
275+
sum.fractionLen = fractionLen;
276+
277+
let sumLimbs = sum.limbList;
278+
let augendLimbs = augend.limbList;
279+
let addendLimbs = addend.limbList;
280+
let posAugend = 0;
281+
let posAddend = 0;
282+
let carry = 0;
283+
let limbSum: number;
284+
285+
// If one input has more fractional limbs, just copy the leftovers to output.
286+
287+
while(posAugend < len) {
288+
sumLimbs[posAugend] = augendLimbs[posAugend];
289+
++posAugend;
290+
}
291+
292+
let lenAddend = addendLimbs.length;
293+
294+
len = augendLimbs.length - posAugend;
295+
if(len > lenAddend) len = lenAddend;
296+
297+
// Calculate sum where input numbers overlap.
298+
299+
while(posAddend < len) {
300+
carry += augendLimbs[posAugend] + addendLimbs[posAddend++];
301+
limbSum = carry >>> 0;
302+
carry = carry - limbSum && 1;
303+
304+
sumLimbs[posAugend++] = limbSum;
305+
}
306+
307+
let posSum = posAugend;
308+
309+
if(len < lenAddend) {
310+
len = lenAddend;
311+
augend = addend;
312+
posAugend = posAddend;
313+
augendLimbs = addendLimbs;
314+
} else len = augendLimbs.length;
315+
316+
// Copy leftover most significant limbs to output, propagating carry.
317+
318+
while(posAugend < len) {
319+
carry += augendLimbs[posAugend++];
320+
limbSum = carry >>> 0;
321+
carry = carry - limbSum && 1;
322+
323+
sumLimbs[posSum++] = limbSum;
324+
}
325+
326+
if(carry) sumLimbs[posSum] = carry;
327+
328+
return(sum);
329+
}
330+
331+
subBig(subtrahend: BigFloat) {
332+
let minuend = this as BigFloat;
333+
let difference = new BigFloat();
334+
335+
difference.isNegative = this.isNegative;
336+
337+
// Make sure the subtrahend is the smaller number.
338+
if(minuend.absDeltaFrom(subtrahend) < 0) {
339+
minuend = subtrahend;
340+
subtrahend = this;
341+
difference.isNegative ^= 1;
342+
}
343+
344+
let fractionLen = minuend.fractionLen;
345+
let len = fractionLen - subtrahend.fractionLen;
346+
347+
let differenceLimbs = difference.limbList;
348+
let minuendLimbs = minuend.limbList;
349+
let subtrahendLimbs = subtrahend.limbList;
350+
let lenMinuend = minuendLimbs.length;
351+
let lenSubtrahend = subtrahendLimbs.length;
352+
let lenFinal = lenMinuend;
353+
let posMinuend = 0;
354+
let posSubtrahend = 0;
355+
let posDifference = 0;
356+
let carry = 0;
357+
let limbDiff: number;
358+
359+
if(len >= 0) {
360+
while(posMinuend < len) {
361+
differenceLimbs[posMinuend] = minuendLimbs[posMinuend];
362+
++posMinuend;
363+
}
364+
365+
len += lenSubtrahend;
366+
if(len > lenMinuend) len = lenMinuend;
367+
368+
posDifference = posMinuend;
369+
} else {
370+
len = -len;
371+
fractionLen += len;
372+
lenFinal += len;
373+
374+
while(posSubtrahend < len) {
375+
carry -= subtrahendLimbs[posSubtrahend];
376+
limbDiff = carry >>> 0;
377+
carry = -(carry < 0);
378+
379+
differenceLimbs[posSubtrahend++] = limbDiff;
380+
}
381+
382+
len += lenMinuend;
383+
if(len > lenSubtrahend) len = lenSubtrahend;
384+
385+
posDifference = posSubtrahend;
386+
}
387+
388+
difference.fractionLen = fractionLen;
389+
390+
// Calculate difference where input numbers overlap.
391+
392+
while(posDifference < len) {
393+
carry += minuendLimbs[posMinuend++] - subtrahendLimbs[posSubtrahend++];
394+
limbDiff = carry >>> 0;
395+
carry = -(carry < 0);
396+
397+
differenceLimbs[posDifference++] = limbDiff;
398+
}
399+
400+
// Copy leftover most significant limbs to output, propagating carry.
401+
402+
while(posDifference < lenFinal) {
403+
carry += minuendLimbs[posMinuend++];
404+
limbDiff = carry >>> 0;
405+
carry = -(carry < 0);
406+
407+
differenceLimbs[posDifference++] = limbDiff;
408+
}
409+
410+
return(difference);
411+
}
412+
413+
private addSub(addend: number | BigFloat, flip: number) {
414+
if(typeof(addend) == 'number') {
415+
addend = BigFloat.tempFloat.setDouble(addend as number);
416+
}
417+
418+
if(this.isNegative ^ (addend as BigFloat).isNegative ^ flip) {
419+
return(this.subBig(addend as BigFloat));
420+
} else {
421+
return(this.addBig(addend as BigFloat));
422+
}
423+
}
424+
425+
add(addend: number | BigFloat) {
426+
return(this.addSub(addend, 0));
427+
}
428+
429+
sub(subtrahend: number | BigFloat) {
430+
return(this.addSub(subtrahend, 1));
431+
}
432+
204433
/** Divide by integer, replacing current value by quotient. Return integer remainder. */
205434

206435
private divInt(divisor: number) {

test/test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,20 +113,43 @@ function test() {
113113
} else bc.stdin.end();
114114
}
115115

116+
function sign(x: number) {
117+
return(x > 0 ? 1 : x < 0 ? -1 : 0);
118+
}
119+
116120
let testList: Test[] = [
117121
() => {
118122
a.setDouble(randDouble());
119123
b.setDouble(randDouble());
120124

121125
return({
122-
expr: a.toString(10).toUpperCase() + ' * ' + b.toString(10).toUpperCase() + '\n',
126+
expr: a.toString(10) + ' * ' + b.toString(10) + '\n',
123127
libResult: a.mul(b).toString(10)
124128
});
129+
},
130+
() => {
131+
a.setDouble(randDouble());
132+
b.setDouble(randDouble());
133+
134+
return({
135+
expr: 'sign(' + a.toString(10) + ' - ' + b.toString(10) + ')\n',
136+
libResult: '' + sign(a.deltaFrom(b))
137+
});
138+
},
139+
() => {
140+
a.setDouble(randDouble());
141+
b.setDouble(randDouble());
142+
143+
return({
144+
expr: a.toString(10) + ' + ' + b.toString(10) + '\n',
145+
libResult: a.add(b).toString(10)
146+
});
125147
}
126148
]
127149

128150
console.log('Fuzz-testing...');
129151

152+
bc.stdin.write('define sign(x) {if(x>0) {return(1);} else if(x<0) {return(-1);} else return(0);}');
130153
bc.stdin.write('scale=1000\n');
131154

132155
test();

0 commit comments

Comments
 (0)