Skip to content

Commit c9d7426

Browse files
authored
Optimize decimalCount32/64 & minor refactoring (#1160)
1 parent 03aff6e commit c9d7426

23 files changed

+1442
-1483
lines changed

std/assembly/util/number.ts

Lines changed: 68 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -108,38 +108,33 @@ const FRC_POWERS: StaticArray<u64> = [
108108
export function decimalCount32(value: u32): u32 {
109109
if (value < 100000) {
110110
if (value < 100) {
111-
return select<u32>(1, 2, value < 10);
111+
return 1 + u32(value >= 10);
112112
} else {
113-
let m = select<u32>(4, 5, value < 10000);
114-
return select<u32>(3, m, value < 1000);
113+
return 3 + u32(value >= 10000) + u32(value >= 1000);
115114
}
116115
} else {
117116
if (value < 10000000) {
118-
return select<u32>(6, 7, value < 1000000);
117+
return 6 + u32(value >= 1000000);
119118
} else {
120-
let m = select<u32>(9, 10, value < 1000000000);
121-
return select<u32>(8, m, value < 100000000);
119+
return 8 + u32(value >= 1000000000) + u32(value >= 100000000);
122120
}
123121
}
124122
}
125123

126124
// Count number of decimals for u64 values
127125
// In our case input value always greater than 2^32-1 so we can skip some parts
128-
export function decimalCount64(value: u64): u32 {
126+
export function decimalCount64High(value: u64): u32 {
129127
if (value < 1000000000000000) {
130128
if (value < 1000000000000) {
131-
let m = select<u32>(11, 12, value < 100000000000);
132-
return select<u32>(10, m, value < 10000000000);
129+
return 10 + u32(value >= 100000000000) + u32(value >= 10000000000);
133130
} else {
134-
let m = select<u32>(14, 15, value < 100000000000000);
135-
return select<u32>(13, m, value < 10000000000000);
131+
return 13 + u32(value >= 100000000000000) + u32(value >= 10000000000000);
136132
}
137133
} else {
138134
if (value < 100000000000000000) {
139-
return select<u32>(16, 17, value < 10000000000000000);
135+
return 16 + u32(value >= 10000000000000000);
140136
} else {
141-
let m = select<u32>(19, 20, value < 10000000000000000000);
142-
return select<u32>(18, m, value < 1000000000000000000);
137+
return 18 + u32(value >= 10000000000000000000) + u32(value >= 1000000000000000000);
143138
}
144139
}
145140
}
@@ -254,10 +249,10 @@ export function utoa32(value: u32): String {
254249
export function itoa32(value: i32): String {
255250
if (!value) return "0";
256251

257-
var sign = value < 0;
252+
var sign = value >>> 31;
258253
if (sign) value = -value;
259254

260-
var decimals = decimalCount32(value) + u32(sign);
255+
var decimals = decimalCount32(value) + sign;
261256
var out = __alloc(decimals << 1, idof<String>());
262257

263258
utoa32_core(out, value, decimals);
@@ -275,7 +270,7 @@ export function utoa64(value: u64): String {
275270
out = __alloc(decimals << 1, idof<String>());
276271
utoa32_core(out, val32, decimals);
277272
} else {
278-
let decimals = decimalCount64(value);
273+
let decimals = decimalCount64High(value);
279274
out = __alloc(decimals << 1, idof<String>());
280275
utoa64_core(out, value, decimals);
281276
}
@@ -285,22 +280,21 @@ export function utoa64(value: u64): String {
285280
export function itoa64(value: i64): String {
286281
if (!value) return "0";
287282

288-
var sign = value < 0;
283+
var sign = u32(value >>> 63);
289284
if (sign) value = -value;
290285

291286
var out: usize;
292287
if (<u64>value <= <u64>u32.MAX_VALUE) {
293288
let val32 = <u32>value;
294-
let decimals = decimalCount32(val32) + u32(sign);
289+
let decimals = decimalCount32(val32) + sign;
295290
out = __alloc(decimals << 1, idof<String>());
296291
utoa32_core(out, val32, decimals);
297292
} else {
298-
let decimals = decimalCount64(value) + u32(sign);
293+
let decimals = decimalCount64High(value) + sign;
299294
out = __alloc(decimals << 1, idof<String>());
300295
utoa64_core(out, value, decimals);
301296
}
302297
if (sign) store<u16>(out, CharCode.MINUS);
303-
304298
return changetype<String>(out); // retains
305299
}
306300

@@ -430,10 +424,10 @@ function grisu2(value: f64, buffer: usize, sign: i32): i32 {
430424

431425
// frexp routine
432426
var uv = reinterpret<u64>(value);
433-
var exp = <i32>((uv & 0x7FF0000000000000) >>> 52);
427+
var exp = i32((uv & 0x7FF0000000000000) >>> 52);
434428
var sid = uv & 0x000FFFFFFFFFFFFF;
435429
var frc = (u64(exp != 0) << 52) + sid;
436-
exp = select<i32>(exp, 1, exp != 0) - (0x3FF + 52);
430+
exp = select<i32>(exp, 1, exp) - (0x3FF + 52);
437431

438432
normalizedBoundaries(frc, exp);
439433
getCachedPower(_exp);
@@ -450,7 +444,7 @@ function grisu2(value: f64, buffer: usize, sign: i32): i32 {
450444
var w_exp = umul64e(exp, exp_pow);
451445

452446
var wp_frc = umul64f(_frc_plus, frc_pow) - 1;
453-
var wp_exp = umul64e(_exp, exp_pow);
447+
var wp_exp = umul64e(_exp, exp_pow);
454448

455449
var wm_frc = umul64f(_frc_minus, frc_pow) + 1;
456450
var delta = wp_frc - wm_frc;
@@ -466,7 +460,7 @@ function genDigits(buffer: usize, w_frc: u64, w_exp: i32, mp_frc: u64, mp_exp: i
466460
var wp_w_frc = mp_frc - w_frc;
467461
var wp_w_exp = mp_exp;
468462

469-
var p1 = <u32>(mp_frc >> one_exp);
463+
var p1 = u32(mp_frc >> one_exp);
470464
var p2 = mp_frc & mask;
471465

472466
var kappa = <i32>decimalCount32(p1);
@@ -598,8 +592,8 @@ export function dtoa_core(buffer: usize, value: f64): i32 {
598592

599593
export function dtoa(value: f64): String {
600594
if (value == 0) return "0.0";
601-
if (!isFinite<f64>(value)) {
602-
if (isNaN<f64>(value)) return "NaN";
595+
if (!isFinite(value)) {
596+
if (isNaN(value)) return "NaN";
603597
return select<String>("-Infinity", "Infinity", value < 0);
604598
}
605599
var buffer = __alloc(MAX_DOUBLE_LENGTH << 1, idof<String>());
@@ -611,66 +605,75 @@ export function dtoa(value: f64): String {
611605
}
612606

613607
export function itoa_stream<T extends number>(buffer: usize, offset: usize, value: T): u32 {
614-
buffer += (offset << 1);
615-
if (!value) {
616-
store<u16>(buffer, CharCode._0);
617-
return 1;
618-
}
619-
var decimals: u32 = 0;
608+
buffer += offset << 1;
609+
var sign: u32 = 0;
620610
if (isSigned<T>()) {
621-
let sign = i32(value < 0);
622-
if (sign) value = changetype<T>(-value);
623-
if (sizeof<T>() <= 4) {
624-
decimals = decimalCount32(value) + <u32>sign;
625-
utoa32_core(buffer, value, decimals);
626-
} else {
627-
if (<u64>value <= <u64>u32.MAX_VALUE) {
628-
let val32 = <u32>value;
629-
decimals = decimalCount32(val32) + <u32>sign;
630-
utoa32_core(buffer, val32, decimals);
611+
sign = u32(value < 0);
612+
if (sign) {
613+
value = changetype<T>(-value);
614+
store<u16>(buffer, CharCode.MINUS);
615+
}
616+
}
617+
if (ASC_SHRINK_LEVEL <= 1) {
618+
if (isSigned<T>()) {
619+
if (sizeof<T>() <= 4) {
620+
if (<u32>value < 10) {
621+
store<u16>(buffer + (sign << 1), value | CharCode._0);
622+
return 1 + sign;
623+
}
631624
} else {
632-
decimals = decimalCount64(value) + <u32>sign;
633-
utoa64_core(buffer, value, decimals);
625+
if (<u64>value < 10) {
626+
store<u16>(buffer + (sign << 1), value | CharCode._0);
627+
return 1 + sign;
628+
}
629+
}
630+
} else {
631+
if (value < 10) {
632+
store<u16>(buffer, value | CharCode._0);
633+
return 1;
634634
}
635635
}
636-
if (sign) store<u16>(buffer, CharCode.MINUS);
636+
}
637+
var decimals = sign;
638+
if (sizeof<T>() <= 4) {
639+
decimals += decimalCount32(value);
640+
utoa32_core(buffer, value, decimals);
637641
} else {
638-
if (sizeof<T>() <= 4) {
639-
decimals = decimalCount32(value);
640-
utoa32_core(buffer, value, decimals);
642+
if (<u64>value <= <u64>u32.MAX_VALUE) {
643+
let val32 = <u32>value;
644+
decimals += decimalCount32(val32);
645+
utoa32_core(buffer, val32, decimals);
641646
} else {
642-
if (<u64>value <= <u64>u32.MAX_VALUE) {
643-
let val32 = <u32>value;
644-
decimals = decimalCount32(val32);
645-
utoa32_core(buffer, val32, decimals);
646-
} else {
647-
decimals = decimalCount64(value);
648-
utoa64_core(buffer, value, decimals);
649-
}
647+
decimals += decimalCount64High(value);
648+
utoa64_core(buffer, value, decimals);
650649
}
651650
}
652651
return decimals;
653652
}
654653

655654
export function dtoa_stream(buffer: usize, offset: usize, value: f64): u32 {
656-
buffer += (offset << 1);
657-
if (value == 0.0) {
655+
buffer += offset << 1;
656+
if (value == 0) {
658657
store<u16>(buffer, CharCode._0);
659658
store<u16>(buffer, CharCode.DOT, 2);
660659
store<u16>(buffer, CharCode._0, 4);
661660
return 3;
662661
}
663-
if (!isFinite<f64>(value)) {
664-
if (isNaN<f64>(value)) {
662+
if (!isFinite(value)) {
663+
if (isNaN(value)) {
665664
store<u16>(buffer, CharCode.N);
666665
store<u16>(buffer, CharCode.a, 2);
667666
store<u16>(buffer, CharCode.N, 4);
668667
return 3;
669668
} else {
670-
let sign = i32(value < 0);
671-
let len = 8 + sign;
672-
memory.copy(buffer, changetype<usize>(select<String>("-Infinity", "Infinity", sign)), len << 1);
673-
return len;
669+
let sign = value < 0;
670+
if (sign) {
671+
store<u16>(buffer, CharCode.MINUS); // -
672+
buffer += 2;
673+
}
674+
store<u64>(buffer, 0x690066006E0049, 0); // ifnI
675+
store<u64>(buffer, 0x7900740069006E, 8); // ytin
676+
return 8 + u32(sign);
674677
}
675678
}
676679
return dtoa_core(buffer, value);

tests/compiler/number.optimized.wat

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -43,44 +43,38 @@
4343
(export "memory" (memory $0))
4444
(start $~start)
4545
(func $~lib/util/number/decimalCount32 (; 1 ;) (param $0 i32) (result i32)
46-
i32.const 1
47-
i32.const 2
4846
local.get $0
4947
i32.const 10
50-
i32.lt_u
51-
select
52-
i32.const 3
53-
i32.const 4
54-
i32.const 5
48+
i32.ge_u
49+
i32.const 1
50+
i32.add
5551
local.get $0
5652
i32.const 10000
57-
i32.lt_u
58-
select
53+
i32.ge_u
54+
i32.const 3
55+
i32.add
5956
local.get $0
6057
i32.const 1000
61-
i32.lt_u
62-
select
58+
i32.ge_u
59+
i32.add
6360
local.get $0
6461
i32.const 100
6562
i32.lt_u
6663
select
67-
i32.const 6
68-
i32.const 7
6964
local.get $0
7065
i32.const 1000000
71-
i32.lt_u
72-
select
73-
i32.const 8
74-
i32.const 9
75-
i32.const 10
66+
i32.ge_u
67+
i32.const 6
68+
i32.add
7669
local.get $0
7770
i32.const 1000000000
78-
i32.lt_u
79-
select
71+
i32.ge_u
72+
i32.const 8
73+
i32.add
8074
local.get $0
8175
i32.const 100000000
82-
i32.lt_u
83-
select
76+
i32.ge_u
77+
i32.add
8478
local.get $0
8579
i32.const 10000000
8680
i32.lt_u
@@ -212,8 +206,8 @@
212206
return
213207
end
214208
local.get $0
215-
i32.const 0
216-
i32.lt_s
209+
i32.const 31
210+
i32.shr_u
217211
local.tee $1
218212
if
219213
i32.const 0

0 commit comments

Comments
 (0)