Skip to content

Commit 7f29418

Browse files
strconv: change atof64 to return an error, if the parsed value is not a valid number (#13424)
1 parent 1c19573 commit 7f29418

File tree

5 files changed

+35
-9
lines changed

5 files changed

+35
-9
lines changed

vlib/builtin/string.v

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,12 +511,12 @@ pub fn (s string) i16() i16 {
511511

512512
// f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`.
513513
pub fn (s string) f32() f32 {
514-
return f32(strconv.atof64(s))
514+
return f32(strconv.atof64(s) or { 0 })
515515
}
516516

517517
// f64 returns the value of the string as f64 `'1.0'.f64() == f64(1)`.
518518
pub fn (s string) f64() f64 {
519-
return strconv.atof64(s)
519+
return strconv.atof64(s) or { 0 }
520520
}
521521

522522
// u8 returns the value of the string as u8 `'1'.u8() == u8(1)`.

vlib/strconv/atof.c.v

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ pub const (
104104
parser_mzero = 2 // number is negative, module smaller
105105
parser_pinf = 3 // number is higher than +HUGE_VAL
106106
parser_minf = 4 // number is lower than -HUGE_VAL
107+
parser_invalid_number = 5 // invalid number, used for '#@%^' for example
107108
//
108109
// char constants
109110
// Note: Modify these if working with non-ASCII encoding
@@ -232,6 +233,9 @@ fn parser(s string) (int, PrepNumber) {
232233
result = strconv.parser_pzero
233234
}
234235
}
236+
if i == 0 && s.len > 0 {
237+
return strconv.parser_invalid_number, pn
238+
}
235239
return result, pn
236240
}
237241

@@ -403,9 +407,9 @@ fn converter(mut pn PrepNumber) u64 {
403407
// Public functions
404408

405409
// atof64 return a f64 from a string doing a parsing operation
406-
pub fn atof64(s string) f64 {
410+
pub fn atof64(s string) ?f64 {
407411
if s.len == 0 {
408-
return 0
412+
return error('expected a number found an empty string')
409413
}
410414
mut pn := PrepNumber{}
411415
mut res_parsing := 0
@@ -428,7 +432,9 @@ pub fn atof64(s string) f64 {
428432
strconv.parser_minf {
429433
res.u = strconv.double_minus_infinity
430434
}
431-
else {}
435+
else {
436+
return error('not a number')
437+
}
432438
}
433439
return unsafe { res.f }
434440
}

vlib/strconv/atof.js.v

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
module strconv
22

33
// atof64 return a f64 from a string doing a parsing operation
4-
pub fn atof64(s string) f64 {
4+
pub fn atof64(s string) ?f64 {
5+
// TODO: handle parsing invalid numbers as close as possible to the pure V version
6+
// that may be slower, but more portable, and will guarantee that higher level code
7+
// works the same in the JS version, as well as in the C and Native versions.
58
res := 0.0
69
#res.val = Number(s.str)
710

vlib/strconv/atof_test.v

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ fn test_atof() {
3636
// check conversion case 1 string <=> string
3737
for c, x in src_num {
3838
// slow atof
39-
assert strconv.atof64(src_num_str[c]).strlong() == x.strlong()
39+
val := strconv.atof64(src_num_str[c]) or { panic(err) }
40+
assert val.strlong() == x.strlong()
4041

4142
// quick atof
4243
mut s1 := (strconv.atof_quick(src_num_str[c]).str())
@@ -56,7 +57,8 @@ fn test_atof() {
5657
// we don't test atof_quick beacuse we already know the rounding error
5758
for c, x in src_num_str {
5859
b := src_num[c].strlong()
59-
a1 := strconv.atof64(x).strlong()
60+
value := strconv.atof64(x) or { panic(err) }
61+
a1 := value.strlong()
6062
assert a1 == b
6163
}
6264

@@ -73,3 +75,18 @@ fn test_atof() {
7375
assert *ptr == u64(0x8000000000000000)
7476
println('DONE!')
7577
}
78+
79+
fn test_atof_errors() {
80+
if x := strconv.atof64('') {
81+
eprintln('> x: $x')
82+
assert false // strconv.atof64 should have failed
83+
} else {
84+
assert err.str() == 'expected a number found an empty string'
85+
}
86+
if x := strconv.atof64('####') {
87+
eprintln('> x: $x')
88+
assert false // strconv.atof64 should have failed
89+
} else {
90+
assert err.str() == 'not a number'
91+
}
92+
}

vlib/v/eval/expr.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object {
191191
}) // TODO: numbers larger than 2^63 (for u64)
192192
}
193193
ast.FloatLiteral {
194-
return f64(strconv.atof64(expr.val))
194+
return f64(strconv.atof64(expr.val) or { e.error(err.str()) })
195195
}
196196
ast.BoolLiteral {
197197
return expr.val

0 commit comments

Comments
 (0)