hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit ec2fe276cfc43df92e9d8ef22db1d166dff0b980
parent 9ffee143ea0da32f6180ca50948b418e6f86e136
Author: Joe Finney <me@spxtr.net>
Date:   Sat,  2 Sep 2023 10:22:00 -0700

strconv: make stof and ftos round-trip safe.

Requires special-casing "nan" and "infinity" in stof.

Signed-off-by: Joe Finney <me@spxtr.net>

Diffstat:
Astrconv/ftostof+test.ha | 30++++++++++++++++++++++++++++++
Mstrconv/stof.ha | 44++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 74 insertions(+), 0 deletions(-)

diff --git a/strconv/ftostof+test.ha b/strconv/ftostof+test.ha @@ -0,0 +1,30 @@ +use math; + +// test round-trip accuracy of ftos followed by stof. +@test fn ftostof() void = { + const tcs: []f64 = [ + 1.0, + 0.0, + 0.1, + -0.0, + 1f64 / 3f64, + 4f64 / 3f64, + + 1f64 / 0f64, + -1f64 / 0f64, + + 0f64 / 0f64, + ]; + for (let i = 0z; i < len(tcs); i += 1) { + const res64 = stof64(f64tos(tcs[i]))!; + const res32 = stof32(f32tos(tcs[i]: f32))!; + // there can be multiple NaNs. only test isnan. + if (math::isnan(tcs[i])) { + assert(math::isnan(res64)); + assert(math::isnan(res32)); + } else { + assert(tcs[i] == res64); + assert(tcs[i]: f32 == res32); + }; + }; +}; diff --git a/strconv/stof.ha b/strconv/stof.ha @@ -530,13 +530,31 @@ fn stof32exact(mant: u32, exp: i32, neg: bool) (f32 | void) = { return n; }; +fn special(s: str) (f32 | void) = { + if (ascii::strcasecmp(s, "nan") == 0) { + return math::NAN; + } else if (ascii::strcasecmp(s, "infinity") == 0) { + return math::INF; + } else if (ascii::strcasecmp(s, "+infinity") == 0) { + return math::INF; + } else if (ascii::strcasecmp(s, "-infinity") == 0) { + return -math::INF; + }; +}; + // Converts a string to a f64. If the string is not syntactically well-formed // floating-point number in base 10, [[invalid]] is returned. If the string // represents a floating-point number that is larger than the largest finite f64 // number, [[overflow]] is returned. Zero is returned if the string represents a // floating-point number that is smaller than the f64 number nearest to zero // with respective sign. +// Recognizes "Infinity", "+Infinity", "-Infinity", and "NaN", case insensitive. export fn stof64(s: str) (f64 | invalid | overflow) = { + match (special(s)) { + case let f: f32 => + return f; + case void => void; + }; const p = fast_parse(s)?; if (p is fast_parsed_float) { const p = p: fast_parsed_float; @@ -565,7 +583,13 @@ export fn stof64(s: str) (f64 | invalid | overflow) = { // number, [[overflow]] is returned. Zero is returned if the string represents a // floating-point number that is smaller than the f32 number nearest to zero // with respective sign. +// Recognizes "Infinity", "+Infinity", "-Infinity", and "NaN", case insensitive. export fn stof32(s: str) (f32 | invalid | overflow) = { + match (special(s)) { + case let f: f32 => + return f; + case void => void; + }; const p = fast_parse(s)?; if (p is fast_parsed_float) { const p = p: fast_parsed_float; @@ -606,6 +630,16 @@ export fn stof32(s: str) (f32 | invalid | overflow) = { assert(stof64(""): invalid: size == 0); assert(stof64("0ZO"): invalid: size == 1); assert(stof64("1.23ezz"): invalid: size == 5); + assert(stof64("Infinity"): f64 == math::INF); + assert(stof64("+Infinity"): f64 == math::INF); + assert(stof64("-Infinity"): f64 == -math::INF); + assert(stof64("infinity"): f64 == math::INF); + assert(stof64("inFinIty"): f64 == math::INF); + assert(stof64("-infinity"): f64 == -math::INF); + assert(stof64("-infiNity"): f64 == -math::INF); + assert(math::isnan(stof64("NaN"): f64)); + assert(math::isnan(stof64("nan"): f64)); + assert(math::isnan(stof64("naN"): f64)); }; @test fn stof32() void = { @@ -624,5 +658,15 @@ export fn stof32(s: str) (f32 | invalid | overflow) = { assert(stof32(""): invalid: size == 0); assert(stof32("0ZO"): invalid: size == 1); assert(stof32("1.23e-zz"): invalid: size == 6); + assert(stof32("Infinity"): f32 == math::INF); + assert(stof32("+Infinity"): f32 == math::INF); + assert(stof32("-Infinity"): f32 == -math::INF); + assert(stof32("infinity"): f32 == math::INF); + assert(stof32("inFinIty"): f32 == math::INF); + assert(stof32("-infinity"): f32 == -math::INF); + assert(stof32("-infiniTy"): f32 == -math::INF); + assert(math::isnan(stof32("NaN"): f32)); + assert(math::isnan(stof32("nan"): f32)); + assert(math::isnan(stof32("naN"): f32)); };