diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 4d7ba3027f..48415eba16 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -636,13 +636,13 @@ pub fn (s string) i64() i64 { // f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`. @[inline] 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)`. @[inline] 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. diff --git a/vlib/strconv/atof.c.v b/vlib/strconv/atof.c.v index c35aabbae6..dcbe35753b 100644 --- a/vlib/strconv/atof.c.v +++ b/vlib/strconv/atof.c.v @@ -114,6 +114,7 @@ enum ParserState { pinf // number is higher than +HUGE_VAL minf // number is lower than -HUGE_VAL invalid_number // invalid number, used for '#@%^' for example + extra_char // extra char after 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 { return ParserState.invalid_number, pn } + if i != s.len { + return ParserState.extra_char, pn + } return result, pn } @@ -387,13 +391,19 @@ fn converter(mut pn PrepNumber) u64 { 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 -pub fn atof64(s string) !f64 { +pub fn atof64(s string, param AtoF64Param) !f64 { if s.len == 0 { return error('expected a number found an empty string') } mut res := Float64u{} - mut res_parsing, mut pn := parser(s) + res_parsing, mut pn := parser(s) match res_parsing { .ok { res.u = converter(mut pn) @@ -410,6 +420,13 @@ pub fn atof64(s string) !f64 { .minf { 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 { return error('not a number') } diff --git a/vlib/strconv/atof_test.c.v b/vlib/strconv/atof_test.c.v index c40a41c421..61d9513ff2 100644 --- a/vlib/strconv/atof_test.c.v +++ b/vlib/strconv/atof_test.c.v @@ -20,6 +20,9 @@ fn test_atof() { 0.0, -0.0, 31234567890123, + 0.01, + 2000, + -300, ] // strings @@ -31,6 +34,9 @@ fn test_atof() { '0.0', '-0.0', '31234567890123', + '1e-2', + '+2e+3', + '-3.0e+2', ] // check conversion case 1 string <=> string @@ -89,4 +95,16 @@ fn test_atof_errors() { } else { 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' + } }