From b2427cdb05535025138fa9f4757c450331f27f48 Mon Sep 17 00:00:00 2001 From: PgBiel <9021226+PgBiel@users.noreply.github.com> Date: Wed, 9 Oct 2024 01:28:24 -0300 Subject: [PATCH] proper exp syntax support for decimals --- oxifmt.typ | 46 +++++++++++++++++++++++++++++++++++++----- tests/strfmt-tests.typ | 7 +++++-- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/oxifmt.typ b/oxifmt.typ index ff6626f..55dabb3 100644 --- a/oxifmt.typ +++ b/oxifmt.typ @@ -267,12 +267,48 @@ assert(_strfmt_is-numeric-type(num), message: "String formatter internal error: Cannot convert '" + repr(num) + "' to a number to obtain its scientific notation representation.") if type(num) == _decimal { - // TODO: 0.000X support + // Normalize signed zero + let num = if num == 0 { _decimal("0") } else { num } let (integral, ..fractional) = str(num).split(".") - let fractional = fractional.join() - let total-digits = integral.len() + fractional.len() - let mantissa = integral.codepoints().first() + "." + integral.slice(1) + fractional - return (mantissa, exponent-sign + _strfmt_stringify(total-digits - 1)) + // Normalize decimals with larger scales than is needed + let fractional = fractional.sum(default: "").trim("0", at: end) + let (integral, fractional, exponent) = if num > -1 and num < 1 and fractional != "" { + let first-non-zero = fractional.position("1") + if first-non-zero == none { first-non-zero = fractional.position("2") } + if first-non-zero == none { first-non-zero = fractional.position("3") } + if first-non-zero == none { first-non-zero = fractional.position("4") } + if first-non-zero == none { first-non-zero = fractional.position("5") } + if first-non-zero == none { first-non-zero = fractional.position("6") } + if first-non-zero == none { first-non-zero = fractional.position("7") } + if first-non-zero == none { first-non-zero = fractional.position("8") } + if first-non-zero == none { first-non-zero = fractional.position("9") } + assert(first-non-zero != none, message: "String formatter internal error: expected non-zero fractional digit") + + // Integral part is zero + // Convert 0.00012345 -> 1.2345 + // Position of first non-zero is the amount of zeroes - 1 + // (e.g. above, position of 1 is 3 => 3 zeroes, + // so exponent is -3 - 1 = -4) + ( + fractional.at(first-non-zero), + fractional.slice(first-non-zero + 1), + -first-non-zero - 1 + ) + } else { + // Number has non-zero integral part, or is zero + // Convert 12345.6789 -> 1.23456789 + // Exponent is integral digits - 1 + ( + integral.at(0), + integral.slice(1) + fractional, + integral.len() - 1 + ) + } + return ( + // mantissa + integral + if fractional != "" { "." + fractional } else { "" }, + exponent-sign + _strfmt_stringify(exponent) + ) } let f = float(num) diff --git a/tests/strfmt-tests.typ b/tests/strfmt-tests.typ index 1752dcb..4291e4d 100644 --- a/tests/strfmt-tests.typ +++ b/tests/strfmt-tests.typ @@ -118,8 +118,11 @@ assert.eq(strfmt("{}", decimal("-1223.435032"), fmt-thousands-separator: "_"), "-1_223.435032") assert.eq(strfmt("{:+09}", decimal("1234.5")), "+001234.5") assert.eq(strfmt("{:+09}", decimal("1234.5"), fmt-thousands-separator: "_"), "+001_234.5") - assert.eq(strfmt("{:011e}", -decimal("1234.5")), "-001.2345e4") - assert.eq(strfmt("{:011e}", -decimal("1234.50000")), "-001.2345e4") + assert.eq(strfmt("{:011e}", -decimal("1234.5")), "-001.2345e3") + assert.eq(strfmt("{:011e}", -decimal("1234.50000")), "-001.2345e3") + assert.eq(strfmt("{:011e}", -decimal("0.00012345")), "-01.2345e-4") + assert.eq(strfmt("{:e}", -decimal("0")), "0e0") + assert.eq(strfmt("{:e}", decimal("132423")), "1.32423e5") assert.eq(strfmt("{:011.5}", decimal("1234.5")), "01234.50000") } // DOC TESTS