strconv: fix strconv.atof64() inconsistency with the other .ato functions; make it return an error by default, when it detects an extra non number character after a number (#23815)

This commit is contained in:
kbkpbot 2025-02-27 14:31:59 +08:00 committed by GitHub
parent 62cbc8befe
commit 675fe14cbb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 39 additions and 4 deletions

View File

@ -636,13 +636,13 @@ pub fn (s string) i64() i64 {
// f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`. // f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`.
@[inline] @[inline]
pub fn (s string) f32() f32 { pub fn (s string) f32() f32 {
return f32(strconv.atof64(s) or { 0 }) return f32(strconv.atof64(s, allow_extra_chars: true) or { 0 })
} }
// f64 returns the value of the string as f64 `'1.0'.f64() == f64(1)`. // f64 returns the value of the string as f64 `'1.0'.f64() == f64(1)`.
@[inline] @[inline]
pub fn (s string) f64() f64 { pub fn (s string) f64() f64 {
return strconv.atof64(s) or { 0 } return strconv.atof64(s, allow_extra_chars: true) or { 0 }
} }
// u8_array returns the value of the hex/bin string as u8 array. // u8_array returns the value of the hex/bin string as u8 array.

View File

@ -114,6 +114,7 @@ enum ParserState {
pinf // number is higher than +HUGE_VAL pinf // number is higher than +HUGE_VAL
minf // number is lower than -HUGE_VAL minf // number is lower than -HUGE_VAL
invalid_number // invalid number, used for '#@%^' for example invalid_number // invalid number, used for '#@%^' for example
extra_char // extra char after number
} }
// parser tries to parse the given string into a number // parser tries to parse the given string into a number
@ -218,6 +219,9 @@ fn parser(s string) (ParserState, PrepNumber) {
if i == 0 && s.len > 0 { if i == 0 && s.len > 0 {
return ParserState.invalid_number, pn return ParserState.invalid_number, pn
} }
if i != s.len {
return ParserState.extra_char, pn
}
return result, pn return result, pn
} }
@ -387,13 +391,19 @@ fn converter(mut pn PrepNumber) u64 {
return result return result
} }
@[params]
pub struct AtoF64Param {
pub:
allow_extra_chars bool // allow extra characters after number
}
// atof64 parses the string `s`, and if possible, converts it into a f64 number // atof64 parses the string `s`, and if possible, converts it into a f64 number
pub fn atof64(s string) !f64 { pub fn atof64(s string, param AtoF64Param) !f64 {
if s.len == 0 { if s.len == 0 {
return error('expected a number found an empty string') return error('expected a number found an empty string')
} }
mut res := Float64u{} mut res := Float64u{}
mut res_parsing, mut pn := parser(s) res_parsing, mut pn := parser(s)
match res_parsing { match res_parsing {
.ok { .ok {
res.u = converter(mut pn) res.u = converter(mut pn)
@ -410,6 +420,13 @@ pub fn atof64(s string) !f64 {
.minf { .minf {
res.u = double_minus_infinity res.u = double_minus_infinity
} }
.extra_char {
if param.allow_extra_chars {
res.u = converter(mut pn)
} else {
return error('extra char after number')
}
}
.invalid_number { .invalid_number {
return error('not a number') return error('not a number')
} }

View File

@ -20,6 +20,9 @@ fn test_atof() {
0.0, 0.0,
-0.0, -0.0,
31234567890123, 31234567890123,
0.01,
2000,
-300,
] ]
// strings // strings
@ -31,6 +34,9 @@ fn test_atof() {
'0.0', '0.0',
'-0.0', '-0.0',
'31234567890123', '31234567890123',
'1e-2',
'+2e+3',
'-3.0e+2',
] ]
// check conversion case 1 string <=> string // check conversion case 1 string <=> string
@ -89,4 +95,16 @@ fn test_atof_errors() {
} else { } else {
assert err.str() == 'not a number' assert err.str() == 'not a number'
} }
if x := strconv.atof64('uu577.01') {
eprintln('> x: ${x}')
assert false // strconv.atof64 should have failed
} else {
assert err.str() == 'not a number'
}
if x := strconv.atof64('123.33xyz') {
eprintln('> x: ${x}')
assert false // strconv.atof64 should have failed
} else {
assert err.str() == 'extra char after number'
}
} }