Skip to content

Commit be1d94a

Browse files
fix: use single precision for dtoa with f32 (#2908)
1 parent 6e151f8 commit be1d94a

18 files changed

+13604
-3232
lines changed

std/assembly/util/number.ts

+41-17
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ export function itoa64(value: i64, radix: i32): String {
474474
@lazy let _frc_minus: u64 = 0;
475475

476476
// @ts-ignore: decorator
477-
@lazy let _frc_plus: u64 = 0;
477+
@lazy let _frc_plus: u64 = 0;
478478

479479
// @ts-ignore: decorator
480480
@lazy let _frc_pow: u64 = 0;
@@ -511,14 +511,14 @@ function umul64e(e1: i32, e2: i32): i32 {
511511

512512
// @ts-ignore: decorator
513513
@inline
514-
function normalizedBoundaries(f: u64, e: i32): void {
514+
function normalizedBoundaries(f: u64, e: i32, isSingle: bool): void {
515515
let frc = (f << 1) + 1;
516516
let exp = e - 1;
517517
let off = <i32>clz<u64>(frc);
518518
frc <<= off;
519519
exp -= off;
520520

521-
let m = 1 + i32(f == 0x0010000000000000);
521+
let m = 1 + i32(f == (isSingle ? 0x00800000 : 0x0010000000000000));
522522

523523
_frc_plus = frc;
524524
_frc_minus = ((f << m) - 1) << e - m - exp;
@@ -559,16 +559,26 @@ function getCachedPower(minExp: i32): void {
559559

560560
// @ts-ignore: decorator
561561
@inline
562-
function grisu2(value: f64, buffer: usize, sign: i32): i32 {
562+
function grisu2(value: f64, buffer: usize, sign: i32, isSingle: bool): i32 {
563+
let frc: u64;
564+
let exp: i32;
563565

564566
// frexp routine
565-
let uv = reinterpret<u64>(value);
566-
let exp = i32((uv & 0x7FF0000000000000) >>> 52);
567-
let sid = uv & 0x000FFFFFFFFFFFFF;
568-
let frc = (u64(exp != 0) << 52) + sid;
569-
exp = select<i32>(exp, 1, exp) - (0x3FF + 52);
567+
if (isSingle) {
568+
let uv = reinterpret<u32>(<f32>value);
569+
exp = (uv & 0x7F800000) >>> 23;
570+
let sid = uv & 0x007FFFFF;
571+
frc = (u64(exp != 0) << 23) + sid;
572+
exp = (exp || 1) - (0x7F + 23);
573+
} else {
574+
let uv = reinterpret<u64>(value);
575+
exp = i32((uv & 0x7FF0000000000000) >>> 52);
576+
let sid = uv & 0x000FFFFFFFFFFFFF;
577+
frc = (u64(exp != 0) << 52) + sid;
578+
exp = (exp || 1) - (0x3FF + 52);
579+
}
570580

571-
normalizedBoundaries(frc, exp);
581+
normalizedBoundaries(frc, exp, isSingle);
572582
getCachedPower(_exp);
573583

574584
// normalize
@@ -716,28 +726,35 @@ function prettify(buffer: usize, length: i32, k: i32): i32 {
716726
}
717727
}
718728

719-
function dtoa_core(buffer: usize, value: f64): i32 {
729+
function dtoa_core(buffer: usize, value: f64, isSingle: bool): i32 {
720730
let sign = i32(value < 0);
721731
if (sign) {
722732
value = -value;
723733
store<u16>(buffer, CharCode.MINUS);
724734
}
725-
// assert(value > 0 && value <= 1.7976931348623157e308);
726-
let len = grisu2(value, buffer, sign);
735+
// assert(value > 0 && value <= (isSingle ? f32.MAX_VALUE : f64.MAX_VALUE));
736+
let len = grisu2(value, buffer, sign, isSingle);
727737
len = prettify(buffer + (sign << 1), len - sign, _K);
728738
return len + sign;
729739
}
730740

731741
// @ts-ignore: decorator
732742
@lazy @inline const dtoa_buf = memory.data(MAX_DOUBLE_LENGTH << 1);
733743

734-
export function dtoa(value: f64): String {
744+
export function dtoa<T extends number>(value: T): String {
745+
const isSingle = isFloat<T>() && sizeof<T>() == 4;
746+
return dtoa_impl(value, isSingle);
747+
}
748+
749+
// @ts-ignore: decorator
750+
@inline
751+
function dtoa_impl(value: f64, isSingle: bool): String {
735752
if (value == 0) return "0.0";
736753
if (!isFinite(value)) {
737754
if (isNaN(value)) return "NaN";
738755
return select<String>("-Infinity", "Infinity", value < 0);
739756
}
740-
let size = dtoa_core(dtoa_buf, value) << 1;
757+
let size = dtoa_core(dtoa_buf, value, isSingle) << 1;
741758
let result = changetype<String>(__new(size, idof<String>()));
742759
memory.copy(changetype<usize>(result), dtoa_buf, size);
743760
return result;
@@ -821,7 +838,14 @@ export function itoa_buffered<T extends number>(buffer: usize, value: T): u32 {
821838
return sign + decimals;
822839
}
823840

824-
export function dtoa_buffered(buffer: usize, value: f64): u32 {
841+
export function dtoa_buffered<T extends number>(buffer: usize, value: T): u32 {
842+
const isSingle = isFloat<T>() && sizeof<T>() == 4;
843+
return dtoa_buffered_impl(buffer, value, isSingle);
844+
}
845+
846+
// @ts-ignore: decorator
847+
@inline
848+
function dtoa_buffered_impl(buffer: usize, value: f64, isSingle: bool): u32 {
825849
if (value == 0) {
826850
store<u16>(buffer, CharCode._0);
827851
store<u16>(buffer, CharCode.DOT, 2);
@@ -845,5 +869,5 @@ export function dtoa_buffered(buffer: usize, value: f64): u32 {
845869
return 8 + u32(sign);
846870
}
847871
}
848-
return dtoa_core(buffer, value);
872+
return dtoa_core(buffer, value, isSingle);
849873
}

0 commit comments

Comments
 (0)