Skip to content

Commit 79d9622

Browse files
committed
Support converting strings to numbers.
1 parent d25918a commit 79d9622

6 files changed

Lines changed: 176 additions & 31 deletions

File tree

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99
`bigfloat` is a fast arbitrary precision math library optimized for computational geometry and geoinformatics.
1010
It provides binary floating point:
1111

12-
- conversion from the JavaScript number type, `x = new BigFloat53(123.456)`
12+
- conversion to / from the JavaScript number type, `x = new BigFloat32(123.456)` and `x.valueOf()`
1313
- addition, `x.add(y)`
1414
- subtraction, `x.sub(y)`
1515
- multiplication, `x.mul(y)`
1616
- comparison, `x.deltaFrom(y)` alias `x.cmp(y)`
17-
- conversion to string in even bases 2-36, `x.toString(10)`
17+
- string output in even bases 2-36, `x.toString(10)`
18+
- string parsing in bases 2-36, `x = new BigFloat32('abc.def', 16)`
1819

1920
without ever losing any significant bits. Numbers are immutable in the above operations, so they return a new BigFloat.
2021
For efficiency, the following methods instead destructively change the value:

src/BaseInfo32.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ export class BaseInfo32 {
2222

2323
private static baseTbl: { [base: number]: BaseInfo32 } = {};
2424

25-
/** Number of digits per limb, for compatibility in rounding. */
26-
limbDigits = Math.log(limbSize32) / Math.log(this.base);
25+
/** Average number of digits per limb. */
26+
limbDigitsExact = Math.log(limbSize32) / Math.log(this.base);
27+
/** Number of entire digits per limb. */
28+
limbDigits = ~~this.limbDigitsExact;
2729
/** Maximum power of base that fits in a limb. */
28-
limbBase = Math.pow(this.base, ~~this.limbDigits);
30+
limbBase = Math.pow(this.base, this.limbDigits);
2931
/** String of zeroes for padding an empty limb. */
30-
pad = zeroes(~~this.limbDigits);
32+
pad = zeroes(this.limbDigits);
3133

3234
}

src/BigComplex.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ function __extends<Child, Parent>(child: new(...args: any[]) => Child, parent: n
1818
export class BigComplex<Base extends BigFloatBase<Base>> {
1919

2020
constructor(
21-
real?: Base | number,
22-
imag?: Base | number
21+
real?: Base | number | string,
22+
imag?: Base | number | string,
23+
base?: number
2324
) {
24-
this.real = typeof(real) == 'object' ? real : new this.Base(real);
25-
this.imag = typeof(imag) == 'object' ? imag : new this.Base(imag);
25+
this.real = typeof(real) == 'object' ? real : new this.Base(real, base);
26+
this.imag = typeof(imag) == 'object' ? imag : new this.Base(imag, base);
2627
}
2728

2829
clone() {
@@ -113,7 +114,7 @@ export class BigComplex<Base extends BigFloatBase<Base>> {
113114
return(this);
114115
}
115116

116-
Base: new(x?: number) => Base;
117+
Base: new(x?: Base | number | string, base?: number) => Base;
117118
real: Base;
118119
imag: Base;
119120

src/BigFloat32.ts

Lines changed: 143 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { trimNumber } from './util';
77

88
export class BigFloat32 implements BigFloatBase<BigFloat32> {
99

10-
constructor(value?: number | BigFloat32) {
11-
value ? this.setValue(value) : this.setZero();
10+
constructor(value?: BigFloat32 | number | string, base?: number) {
11+
value ? this.setValue(value, base) : this.setZero();
1212
}
1313

1414
clone() {
@@ -23,12 +23,16 @@ export class BigFloat32 implements BigFloatBase<BigFloat32> {
2323
return(this);
2424
}
2525

26-
setValue(other: number | BigFloat32) {
26+
setValue(other: BigFloat32 | number | string, base?: number) {
2727
if(typeof(other) == 'number') {
2828
return(this.setNumber(other));
2929
}
3030

31-
return(this.setBig(other));
31+
if(other instanceof BigFloat32) {
32+
return(this.setBig(other));
33+
}
34+
35+
return(this.setString(other.toString(), base || 10));
3236
}
3337

3438
private setBig(other: BigFloat32) {
@@ -87,8 +91,10 @@ export class BigFloat32 implements BigFloatBase<BigFloat32> {
8791
len += pos + 1;
8892

8993
// Handle integer part.
94+
9095
while(iPart) {
9196
// Extract limbs starting from the least significant.
97+
9298
limb = iPart % limbSize32; // Could also be iPart >>> 0
9399
iPart = (iPart - limb) / limbSize32;
94100

@@ -103,6 +109,112 @@ export class BigFloat32 implements BigFloatBase<BigFloat32> {
103109
return(this);
104110
}
105111

112+
private parseFraction(value: string, base: number, start: number, offset: number, limbBase: number, limbDigits: number) {
113+
const limbList = this.limbList;
114+
let pos = value.length;
115+
116+
// Set limbs to zero, because divInt uses them as input.
117+
118+
let limbNum = offset - 1;
119+
120+
while(limbNum) {
121+
limbList[--limbNum] = 0;
122+
}
123+
124+
// Read initial digits so their count becomes divisible by limbDigits.
125+
126+
let posNext = pos - ((pos - start + limbDigits - 1) % limbDigits + 1);
127+
128+
limbList[offset - 1] = parseInt(value.substr(posNext, pos - posNext), base);
129+
this.divInt(Math.pow(base, pos - posNext), offset);
130+
131+
pos = posNext;
132+
133+
// Read rest of the digits in limbDigits sized chunks.
134+
135+
while(pos > start) {
136+
pos -= limbDigits;
137+
138+
limbList[offset - 1] = parseInt(value.substr(pos, limbDigits), base);
139+
140+
// Divide by maximum power of base that fits in a limb.
141+
this.divInt(limbBase, offset);
142+
}
143+
}
144+
145+
private setString(value: string, base: number) {
146+
const { limbBase, limbDigits, limbDigitsExact } = BaseInfo32.init(base);
147+
const limbList = this.limbList;
148+
let pos = -1;
149+
let c: string;
150+
151+
this.sign = 1;
152+
153+
// Handle leading signs and zeroes.
154+
155+
while(1) {
156+
c = value.charAt(++pos);
157+
158+
switch(c) {
159+
case '-':
160+
this.sign = -1;
161+
case '+':
162+
case '0':
163+
continue;
164+
}
165+
166+
break;
167+
}
168+
169+
const posDot = (value.indexOf('.', pos) + 1 || value.length + 1) - 1;
170+
171+
// Handle fractional part.
172+
173+
if(posDot < value.length - 1) {
174+
// Reserve enough limbs to contain digits in fractional part.
175+
const len = ~~((value.length - posDot - 1) / limbDigitsExact) + 1;
176+
177+
this.parseFraction(value, base, posDot + 1, len + 1, limbBase, limbDigits);
178+
179+
this.fractionLen = len;
180+
this.len = len;
181+
182+
// Remove trailing zeroes.
183+
this.trimLeast();
184+
} else {
185+
this.fractionLen = 0;
186+
this.len = 0;
187+
}
188+
189+
const offset = this.fractionLen;
190+
191+
// Handle integer part.
192+
193+
if(posDot > pos) {
194+
// Read initial digits so their count becomes divisible by limbDigits.
195+
196+
let posNext = pos + (posDot - pos + limbDigits - 1) % limbDigits + 1;
197+
198+
++this.len;
199+
limbList[offset] = parseInt(value.substr(pos, posNext - pos), base);
200+
pos = posNext;
201+
202+
// Read rest of the digits in limbDigits sized chunks.
203+
204+
while(pos < posDot) {
205+
// Multiply by maximum power of base that fits in a limb.
206+
if(this.mulInt(limbBase, limbList, offset, offset, 0)) ++this.len;
207+
208+
// Add latest limb.
209+
limbList[offset] += parseInt(value.substr(pos, limbDigits), base);
210+
211+
pos += limbDigits;
212+
}
213+
}
214+
215+
return(this);
216+
}
217+
106218
/** Trim zero limbs from most significant end. */
107219

108220
private trimMost() {
@@ -481,21 +593,20 @@ export class BigFloat32 implements BigFloatBase<BigFloat32> {
481593

482594
/** Divide by integer, replacing current value by quotient. Return integer remainder. */
483595

484-
private divInt(divisor: number) {
596+
private divInt(divisor: number, pos: number) {
485597
let limbList = this.limbList;
486-
let limbNum = this.len;
487598
let limb: number;
488599
let hi: number, lo: number;
489600
let carry = 0;
490601

491602
// If most significant limb is zero after dividing, decrement number of limbs remaining.
492-
if(limbList[limbNum - 1] < divisor) {
493-
carry = limbList[--limbNum];
494-
this.len = limbNum;
603+
if(limbList[pos - 1] < divisor) {
604+
carry = limbList[--pos];
605+
this.len = pos;
495606
}
496607

497-
while(limbNum--) {
498-
limb = limbList[limbNum];
608+
while(pos--) {
609+
limb = limbList[pos];
499610

500611
carry = carry * 0x10000 + (limb >>> 16);
501612
hi = (carry / divisor) >>> 0;
@@ -505,7 +616,7 @@ export class BigFloat32 implements BigFloatBase<BigFloat32> {
505616
lo = (carry / divisor) >>> 0;
506617
carry = carry - lo * divisor;
507618

508-
limbList[limbNum] = ((hi << 16) | lo) >>> 0;
619+
limbList[pos] = ((hi << 16) | lo) >>> 0;
509620
}
510621

511622
return(carry);
@@ -545,11 +656,29 @@ export class BigFloat32 implements BigFloatBase<BigFloat32> {
545656
}
546657
}
547658

659+
getExpansion(output: number[]) {
660+
const limbList = this.limbList;
661+
const len = this.len;
662+
let exp = this.sign;
663+
let pos = this.fractionLen;
664+
665+
while(pos--) {
666+
exp *= limbInv32;
667+
}
668+
669+
while(++pos < len) {
670+
output[pos] = limbList[pos] * exp;
671+
exp *= limbSize32;
672+
}
673+
674+
return(len);
675+
}
676+
548677
valueOf() {
549678
const limbList = this.limbList;
550-
let len = this.fractionLen;
551679
let result = 0;
552680
let exp = limbInv32 * this.sign;
681+
let len = this.fractionLen;
553682
let pos = 0;
554683

555684
while(pos < len) {
@@ -584,7 +713,7 @@ export class BigFloat32 implements BigFloatBase<BigFloat32> {
584713

585714
// Loop while 2 or more limbs remain, requiring arbitrary precision division to extract digits.
586715
while(iPart.len > 1) {
587-
limbStr = iPart.divInt(limbBase).toString(base);
716+
limbStr = iPart.divInt(limbBase, iPart.len).toString(base);
588717

589718
// Prepend digits into final result, padded with zeroes to 9 digits.
590719
// Since more limbs still remain, whole result will not have extra padding.

src/BigFloat53.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ export class BigFloat53 implements BigFloatBase<BigFloat53> {
7272
/** @param value Initial value, a plain JavaScript floating point number
7373
* (IEEE 754 double precision). */
7474

75-
constructor(value?: number | BigFloat53) {
76-
if(value) this.setValue(value);
75+
constructor(value?: BigFloat53 | number | string, base?: number) {
76+
if(value) this.setValue(value, base);
7777
}
7878

7979
clone() {
@@ -90,12 +90,15 @@ export class BigFloat53 implements BigFloatBase<BigFloat53> {
9090
return(this);
9191
}
9292

93-
setValue(other: number | BigFloat53) {
93+
setValue(other: BigFloat53 | number | string, base?: number) {
9494
if(typeof(other) == 'number') {
9595
return(this.setNumber(other));
9696
}
97+
if(other instanceof BigFloat53) {
98+
return(this.setBig(other));
99+
}
97100

98-
return(this.setBig(other));
101+
return(this.setString(other.toString(), base || 10));
99102
}
100103

101104
private setBig(other: BigFloat53) {
@@ -123,6 +126,15 @@ export class BigFloat53 implements BigFloatBase<BigFloat53> {
123126
return(this);
124127
}
125128

129+
private setString(value: string, base: number) {
130+
temp32[0].setValue(value, base);
131+
132+
this.len = temp32[0].getExpansion(this.limbList);
133+
this.normalize();
134+
135+
return(this);
136+
}
137+
126138
/** Set value to the sum of two JavaScript numbers.
127139
*
128140
* @param a Augend.

src/BigFloatBase.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
// Released under the MIT license, see LICENSE.
33

44
export interface BigFloatBase<Type> {
5-
65
clone(): Type;
76
setZero(): Type;
8-
setValue(value: number | Type): Type;
7+
setValue(value: Type | number | string, base?: number): Type;
98
mul(multiplier: number | Type, product?: Type): Type;
109
// absDeltaFrom(other: number | Type): Type;
1110
cmp(other: number | Type): number;
@@ -19,4 +18,5 @@ export interface BigFloatBase<Type> {
1918
round(decimalCount: number): Type;
2019
valueOf(): number;
2120
toString(base: number): string;
21+
2222
}

0 commit comments

Comments
 (0)