Skip to content

Commit 2a0fd33

Browse files
committed
improve bignum parsing
1 parent 2dd726f commit 2a0fd33

6 files changed

Lines changed: 195 additions & 205 deletions

File tree

dub.sdl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ dependency "mir-core" version=">=1.1.106"
99

1010
// versions "TeslAlgoM"
1111

12+
1213
buildType "unittest" {
1314
buildOptions "unittests" "debugMode" "debugInfo"
14-
versions "mir_bignum_test" "mir_bignum_test_llv" // "mir_ndslice_test" "mir_test"
15+
versions "mir_bignum_test" "mir_bignum_test_llv"
16+
versions "mir_ndslice_test"
17+
versions "mir_test"
1518
dflags "-lowmem"
1619
}
1720
buildType "unittest-dip1008" {

source/mir/bignum/decimal.d

Lines changed: 74 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,11 @@ struct Decimal(uint size64)
213213
}
214214
}
215215

216-
uint d = str[0] - '0';
216+
ulong d = str[0] - C('0');
217217
str = str[1 .. $];
218218
exponent = 0;
219219

220-
ulong v;
220+
ulong v, multplier = void;
221221

222222
if (_expect(d >= 10, false))
223223
{
@@ -228,7 +228,7 @@ struct Decimal(uint size64)
228228
if (str.length == 0)
229229
return false;
230230
key = DecimalExponentKey.dot;
231-
d = str[0] - '0';
231+
d = str[0] - C('0');
232232
str = str[1 .. $];
233233
if (_expect(d < 10, true))
234234
goto FI;
@@ -247,7 +247,7 @@ struct Decimal(uint size64)
247247
{
248248
if (str.length == 0)
249249
goto R;
250-
d = str[0] - '0';
250+
d = str[0] - C('0');
251251
str = str[1 .. $];
252252
if (d < 10)
253253
return false;
@@ -260,15 +260,15 @@ struct Decimal(uint size64)
260260
S:
261261
if (str.length == 0)
262262
goto R;
263-
d = str[0] - '0';
263+
d = str[0] - C('0');
264264
str = str[1 .. $];
265265

266266
if (d < 10)
267267
{
268268
F0:
269269
import mir.checkedint: mulu, addu;
270270
bool overflow;
271-
v = mulu(v, cast(uint)10, overflow);
271+
v = mulu(v, 10u, overflow);
272272
if (overflow)
273273
return false;
274274
v = addu(v, d, overflow);
@@ -283,7 +283,7 @@ struct Decimal(uint size64)
283283
{
284284
if (str.length == 0)
285285
return false;
286-
d = str[0] - '0';
286+
d = str[0] - C('0');
287287
str = str[1 .. $];
288288
if (_expect(d < 10, true))
289289
goto F0;
@@ -314,51 +314,76 @@ struct Decimal(uint size64)
314314
import mir.bignum.internal.parse: isMadeOfEightDigits, parseEightDigits;
315315
if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
316316
{
317-
ulong multplier = 100000000;
318-
ulong value = parseEightDigits(str[0 .. 8]);
317+
multplier = 100000000;
318+
d = parseEightDigits(str[0 .. 8]);
319319
str = str[8 .. $];
320320
exponentShift -= 8;
321-
if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
321+
if (str.length >= 7)
322322
{
323-
multplier = 100000000 * 100000000;
324-
value *= 100000000;
325-
value += parseEightDigits(str[0 .. 8]);
326-
str = str[8 .. $];
327-
exponentShift -= 8;
328-
}
329-
330-
{
331-
import mir.checkedint: mulu, addu;
332-
bool overflow;
333-
v = mulu(v, multplier, overflow);
334-
if (overflow)
335-
return false;
336-
v = addu(v, value, overflow);
337-
if (overflow)
338-
return false;
323+
if (isMadeOfEightDigits((str.ptr - 1)[0 .. 8]))
324+
{
325+
multplier = 100000000 * 10000000;
326+
d -= str.ptr[-1] - '0';
327+
d *= 10000000;
328+
d += parseEightDigits((str.ptr - 1)[0 .. 8]);
329+
str = str[7 .. $];
330+
exponentShift -= 7;
331+
if (str.length)
332+
{
333+
auto md = str[0] - C('0');
334+
if (md < 10)
335+
{
336+
d *= 10;
337+
multplier = 100000000 * 100000000;
338+
d += md;
339+
str = str[1 .. $];
340+
}
341+
}
342+
}
343+
else
344+
{
345+
TrySix:
346+
if (isMadeOfEightDigits((str.ptr - 2)[0 .. 8]))
347+
{
348+
multplier = 100000000 * 1000000;
349+
d -= str.ptr[-1] - '0';
350+
d -= (str.ptr[-2] - '0') * 10;
351+
d *= 1000000;
352+
d += parseEightDigits((str.ptr - 2)[0 .. 8]);
353+
str = str[6 .. $];
354+
exponentShift -= 6;
355+
}
356+
}
357+
339358
}
359+
else
360+
if (str.length == 6)
361+
goto TrySix;
362+
goto FIL;
340363
}
341364
}
342365

343-
d = str[0] - '0';
366+
d = str[0] - C('0');
344367
str = str[1 .. $];
345368
if (_expect(d >= 10, false))
346369
goto DOB;
347370
FI:
371+
exponentShift--;
372+
multplier = 10;
373+
FIL:
348374
{
349375
import mir.checkedint: mulu, addu;
350376
bool overflow;
351-
v = mulu(v, cast(uint)10, overflow);
377+
v = mulu(v, multplier, overflow);
352378
if (overflow)
353379
return false;
354380
v = addu(v, d, overflow);
355381
if (overflow)
356382
return false;
357383
}
358-
exponentShift--;
359384
if (str.length == 0)
360385
goto E;
361-
d = str[0] - '0';
386+
d = str[0] - C('0');
362387
str = str[1 .. $];
363388
if (d < 10)
364389
goto FI;
@@ -369,7 +394,7 @@ struct Decimal(uint size64)
369394
{
370395
if (str.length == 0)
371396
return false;
372-
d = str[0] - '0';
397+
d = str[0] - C('0');
373398
str = str[1 .. $];
374399
if (_expect(d < 10, true))
375400
goto FI;
@@ -817,42 +842,43 @@ unittest
817842

818843
// Check precise percentate parsing
819844
assert(decimal.fromStringImpl("71.7", key, -2));
820-
assert(key == DecimalExponentKey.dot);
845+
key.should == DecimalExponentKey.dot;
821846
// The result is exact value instead of 0.7170000000000001 = 71.7 / 100
822-
assert(cast(double) decimal == 0.717);
847+
(cast(double) decimal).should == 0.717;
823848

824849
assert(decimal.fromStringImpl("+0.334e-5"w, key));
825-
assert(key == DecimalExponentKey.e);
826-
assert(cast(double) decimal == 0.334e-5);
850+
key.should == DecimalExponentKey.e;
851+
(cast(double) decimal).should == 0.334e-5;
827852

828853
assert(decimal.fromStringImpl("100_000_000"w, key));
829-
assert(key == DecimalExponentKey.none);
830-
assert(cast(double) decimal == 1e8);
854+
key.should == DecimalExponentKey.none;
855+
(cast(double) decimal).should == 1e8;
831856

832857
assert(decimal.fromStringImpl("-334D-5"d, key));
833-
assert(key == DecimalExponentKey.D);
834-
assert(cast(double) decimal == -334e-5);
858+
key.should == DecimalExponentKey.D;
859+
(cast(double) decimal).should == -334e-5;
835860

836861
assert(decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key));
837-
assert(key == DecimalExponentKey.none);
838-
assert(cast(double) decimal == 2482734692817364218734682973648217364981273648923423.0);
862+
key.should == DecimalExponentKey.none;
863+
(cast(double) decimal).should == 2482734692817364218734682973648217364981273648923423.0;
839864

840865
assert(decimal.fromStringImpl(".023", key));
841-
assert(key == DecimalExponentKey.dot);
842-
assert(cast(double) decimal == .023);
866+
key.should == DecimalExponentKey.dot;
867+
(cast(double) decimal).should == .023;
843868

844869
assert(decimal.fromStringImpl("0E100", key));
845-
assert(key == DecimalExponentKey.E);
846-
assert(cast(double) decimal == 0);
870+
key.should == DecimalExponentKey.E;
871+
(cast(double) decimal).should == 0;
847872

848873
foreach (str; ["-nan", "-NaN", "-NAN"])
849874
{
850875
assert(decimal.fromStringImpl(str, key));
851876
assert(decimal.coefficient.length > 0);
852877
assert(decimal.exponent == decimal.exponent.max);
853878
assert(decimal.coefficient.sign);
854-
assert(key == DecimalExponentKey.nan);
855-
assert(cast(double) decimal != cast(double) decimal);
879+
key.should == DecimalExponentKey.nan;
880+
auto nan = cast(double) decimal;
881+
(cast(double) decimal).should == double.nan;
856882
}
857883

858884
foreach (str; ["inf", "Inf", "INF"])
@@ -861,7 +887,7 @@ unittest
861887
assert(decimal.coefficient.length == 0);
862888
assert(decimal.exponent == decimal.exponent.max);
863889
assert(key == DecimalExponentKey.infinity);
864-
assert(cast(double) decimal == double.infinity);
890+
(cast(double) decimal).should == double.infinity;
865891
}
866892

867893
assert(decimal.fromStringImpl("-inf", key));

source/mir/bignum/internal/dec2float.d

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,6 @@ private T algorithmM(T)(scope const size_t[] coefficients, long exponent)
258258
{
259259
pragma(inline, false);
260260

261-
// import mir.stdio;
262-
// debug dump("algorithmM", coefficients, exponent);
263-
264261
import mir.bitop: ctlz;
265262
import mir.bignum.fp: Fp;
266263
import mir.bignum.integer: BigInt;

source/mir/bignum/internal/parse.d

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
module mir.bignum.internal.parse;
22

3+
/+
4+
https://arxiv.org/abs/2101.11408
5+
Number Parsing at a Gigabyte per Second
6+
Daniel Lemire
7+
+/
38
bool isMadeOfEightDigits()(ref const char[8] chars)
49
{
510
pragma(inline, true);
611
ulong val = (cast(ulong[1]) cast(ubyte[8]) chars)[0];
712
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & 0x8080808080808080));
813
}
914

15+
// ditto
1016
uint parseEightDigits()(ref const char[8] chars)
1117
{
1218
pragma(inline, true);

0 commit comments

Comments
 (0)