1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

builtin,strconv: append ".0", to float string representations, to ensure clarity (#16079)

This commit is contained in:
Subhomoy Haldar 2022-10-17 13:41:07 +01:00 committed by GitHub
parent 29b1796791
commit 43b9a716c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 98 additions and 74 deletions

View File

@ -1,6 +1,7 @@
## V 0.3.2 ## V 0.3.2
*Not yet released* *Not yet released*
- Remove the need for the `[console]` attribute in Windows GUI apps. - Remove the need for the `[console]` attribute in Windows GUI apps.
- All floats outputs now have `.0` conditionally appended to them to improve clarity.
## V 0.3.1 ## V 0.3.1
*31 Aug 2022* *31 Aug 2022*

View File

@ -19,10 +19,10 @@ pub fn (x f64) str() string {
f: x f: x
} }
if f.u == strconv.double_minus_zero { if f.u == strconv.double_minus_zero {
return '-0' return '-0.0'
} }
if f.u == strconv.double_plus_zero { if f.u == strconv.double_plus_zero {
return '0' return '0.0'
} }
} }
abs_x := f64_abs(x) abs_x := f64_abs(x)
@ -37,11 +37,11 @@ pub fn (x f64) str() string {
[inline] [inline]
pub fn (x f64) strg() string { pub fn (x f64) strg() string {
if x == 0 { if x == 0 {
return '0' return '0.0'
} }
abs_x := f64_abs(x) abs_x := f64_abs(x)
if abs_x >= 0.0001 && abs_x < 1.0e6 { if abs_x >= 0.0001 && abs_x < 1.0e6 {
return strconv.f64_to_str_l_no_dot(x) return strconv.f64_to_str_l_with_dot(x)
} else { } else {
return strconv.ftoa_64(x) return strconv.ftoa_64(x)
} }
@ -85,10 +85,10 @@ pub fn (x f32) str() string {
f: x f: x
} }
if f.u == strconv.single_minus_zero { if f.u == strconv.single_minus_zero {
return '-0' return '-0.'
} }
if f.u == strconv.single_plus_zero { if f.u == strconv.single_plus_zero {
return '0' return '0.'
} }
} }
abs_x := f32_abs(x) abs_x := f32_abs(x)
@ -103,11 +103,11 @@ pub fn (x f32) str() string {
[inline] [inline]
pub fn (x f32) strg() string { pub fn (x f32) strg() string {
if x == 0 { if x == 0 {
return '0' return '0.0'
} }
abs_x := f32_abs(x) abs_x := f32_abs(x)
if abs_x >= 0.0001 && abs_x < 1.0e6 { if abs_x >= 0.0001 && abs_x < 1.0e6 {
return strconv.f32_to_str_l_no_dot(x) return strconv.f32_to_str_l_with_dot(x)
} else { } else {
return strconv.ftoa_32(x) return strconv.ftoa_32(x)
} }

View File

@ -2,7 +2,7 @@ fn test_map_of_f32() {
mut m32 := map[f32]string{} mut m32 := map[f32]string{}
m32[1.0] = 'one' m32[1.0] = 'one'
println(m32) println(m32)
assert '$m32' == r"{1.: 'one'}" assert '$m32' == r"{1.0: 'one'}"
for k, v in m32 { for k, v in m32 {
assert typeof(k).name == 'f32' assert typeof(k).name == 'f32'
assert typeof(v).name == 'string' assert typeof(v).name == 'string'
@ -17,7 +17,7 @@ fn test_map_of_f64() {
} }
m64[1.0] = 'one' m64[1.0] = 'one'
println(m64) println(m64)
assert '$m64' == r"{3.14: 'pi', 1.: 'one'}" assert '$m64' == r"{3.14: 'pi', 1.0: 'one'}"
for k, v in m64 { for k, v in m64 {
assert typeof(k).name == 'f64' assert typeof(k).name == 'f64'
assert typeof(v).name == 'string' assert typeof(v).name == 'string'

View File

@ -190,11 +190,11 @@ fn test_signed_cast() {
mut u := strconv.Float64u{ mut u := strconv.Float64u{
u: strconv.double_plus_zero u: strconv.double_plus_zero
} }
assert '${u.f:g}' == '0' assert '${u.f:g}' == '0.0'
assert '${u.f:G}' == '0' assert '${u.f:G}' == '0.0'
u.u = strconv.double_minus_zero u.u = strconv.double_minus_zero
assert '${u.f:g}' == '0' assert '${u.f:g}' == '0.0'
assert '${u.f:G}' == '0' assert '${u.f:G}' == '0.0'
u.u = strconv.double_plus_infinity u.u = strconv.double_plus_infinity
assert '${u.f:g}' == '+inf' assert '${u.f:g}' == '+inf'
assert '${u.f:G}' == '+INF' assert '${u.f:G}' == '+INF'
@ -206,11 +206,11 @@ fn test_signed_cast() {
mut u := strconv.Float32u{ mut u := strconv.Float32u{
u: strconv.single_plus_zero u: strconv.single_plus_zero
} }
assert '${u.f:g}' == '0' assert '${u.f:g}' == '0.0'
assert '${u.f:G}' == '0' assert '${u.f:G}' == '0.0'
u.u = strconv.single_minus_zero u.u = strconv.single_minus_zero
assert '${u.f:g}' == '0' assert '${u.f:g}' == '0.0'
assert '${u.f:G}' == '0' assert '${u.f:G}' == '0.0'
u.u = strconv.single_plus_infinity u.u = strconv.single_plus_infinity
assert '${u.f:g}' == '+inf' assert '${u.f:g}' == '+inf'
assert '${u.f:G}' == '+INF' assert '${u.f:G}' == '+INF'

View File

@ -425,7 +425,7 @@ fn test_abs_zero() {
ret2 := abs(0.0) ret2 := abs(0.0)
println(ret2) println(ret2)
assert '$ret2' == '0' assert '$ret2' == '0.0'
} }
fn test_floor() { fn test_floor() {

View File

@ -138,7 +138,7 @@ fn test_float_to_str() {
// test f32 // test f32
for c, x in test_cases_f32 { for c, x in test_cases_f32 {
println(x) // println(x)
s := strconv.f32_to_str(x, 8) s := strconv.f32_to_str(x, 8)
s1 := exp_result_f32[c] s1 := exp_result_f32[c]
// println("$s1 $s") // println("$s1 $s")
@ -154,10 +154,13 @@ fn test_float_to_str() {
} }
// test long format // test long format
for exp := 1; exp < 120; exp++ { assert strconv.f64_to_str_l('1e1'.f64()).len == 4 // '10.0'
assert strconv.f64_to_str_l('1e-1'.f64()).len == 3 // '0.1'
for exp := 2; exp < 120; exp++ {
a := strconv.f64_to_str_l(('1e' + exp.str()).f64()) a := strconv.f64_to_str_l(('1e' + exp.str()).f64())
// println(a) // println(a)
assert a.len == exp + 1 assert a.len == exp + 3
b := strconv.f64_to_str_l(('1e-' + exp.str()).f64()) b := strconv.f64_to_str_l(('1e-' + exp.str()).f64())
// println(b) // println(b)

View File

@ -36,14 +36,14 @@ pub fn f32_to_str_l(f f32) string {
return res return res
} }
// f32_to_str_l_no_dot returns `f` as a `string` in decimal notation with a maximum of 6 digits after the dot. // f32_to_str_l_with_dot returns `f` as a `string` in decimal notation with a maximum of 6 digits after the dot.
// The decimal digits after the dot can be omitted. // If the decimal digits after the dot are zero, a '.0' is appended for clarity.
// //
// Example: assert strconv.f32_to_str_l_no_dot(34.) == '34' // Example: assert strconv.f32_to_str_l_with_dot(34.) == '34.0'
[manualfree] [manualfree]
pub fn f32_to_str_l_no_dot(f f32) string { pub fn f32_to_str_l_with_dot(f f32) string {
s := f32_to_str(f, 6) s := f32_to_str(f, 6)
res := fxx_to_str_l_parse_no_dot(s) res := fxx_to_str_l_parse_with_dot(s)
unsafe { s.free() } unsafe { s.free() }
return res return res
} }
@ -59,14 +59,14 @@ pub fn f64_to_str_l(f f64) string {
return res return res
} }
// f64_to_str_l_no_dot returns `f` as a `string` in decimal notation with a maximum of 18 digits after the dot. // f64_to_str_l_with_dot returns `f` as a `string` in decimal notation with a maximum of 18 digits after the dot.
// The decimal digits after the dot can be omitted. // If the decimal digits after the dot are zero, a '.0' is appended for clarity.
// //
// Example: assert strconv.f64_to_str_l_no_dot (34.) == '34' // Example: assert strconv.f64_to_str_l_with_dot (34.) == '34.0'
[manualfree] [manualfree]
pub fn f64_to_str_l_no_dot(f f64) string { pub fn f64_to_str_l_with_dot(f f64) string {
s := f64_to_str(f, 18) s := f64_to_str(f, 18)
res := fxx_to_str_l_parse_no_dot(s) res := fxx_to_str_l_parse_with_dot(s)
unsafe { s.free() } unsafe { s.free() }
return res return res
} }
@ -187,23 +187,30 @@ pub fn fxx_to_str_l_parse(s string) string {
i++ i++
} }
} }
/*
// remove the dot form the numbers like 2. // Add a zero after the dot from the numbers like 2.
if r_i > 1 && res[r_i-1] == `.` { if r_i > 1 && res[r_i - 1] == `.` {
r_i-- res[r_i] = `0`
r_i++
} else if `.` !in res {
// If there is no dot, add it with a zero
res[r_i] = `.`
r_i++
res[r_i] = `0`
r_i++
} }
*/
res[r_i] = 0 res[r_i] = 0
return unsafe { tos(res.data, r_i) } return unsafe { tos(res.data, r_i) }
} }
// fxx_to_str_l_parse_no_dot returns a `string` in decimal notation converted from a // fxx_to_str_l_parse_with_dot returns a `string` in decimal notation converted from a
// floating-point `string` in scientific notation. // floating-point `string` in scientific notation.
// The decimal digits after the dot can be omitted. // If the decimal digits after the dot are zero, a '.0' is appended for clarity.
// //
// Example: assert strconv.fxx_to_str_l_parse_no_dot ('34.e+01') == '340' // Example: assert strconv.fxx_to_str_l_parse_with_dot ('34.e+01') == '340.0'
[direct_array_access; manualfree] [direct_array_access; manualfree]
pub fn fxx_to_str_l_parse_no_dot(s string) string { pub fn fxx_to_str_l_parse_with_dot(s string) string {
// check for +inf -inf Nan // check for +inf -inf Nan
if s.len > 2 && (s[0] == `n` || s[1] == `i`) { if s.len > 2 && (s[0] == `n` || s[1] == `i`) {
return s.clone() return s.clone()
@ -315,9 +322,16 @@ pub fn fxx_to_str_l_parse_no_dot(s string) string {
} }
} }
// remove the dot form the numbers like 2. // Add a zero after the dot from the numbers like 2.
if r_i > 1 && res[r_i - 1] == `.` { if r_i > 1 && res[r_i - 1] == `.` {
r_i-- res[r_i] = `0`
r_i++
} else if `.` !in res {
// If there is no dot, add it with a zero
res[r_i] = `.`
r_i++
res[r_i] = `0`
r_i++
} }
res[r_i] = 0 res[r_i] = 0

View File

@ -238,10 +238,16 @@ fn to_burntsushi(value ast.Value) string {
} }
if !value.text.starts_with('0x') if !value.text.starts_with('0x')
&& (value.text.contains('.') || value.text.to_lower().contains('e')) { && (value.text.contains('.') || value.text.to_lower().contains('e')) {
mut val := '$value.f64()'.replace('.e+', '.0e') // json notation mut val := '$value.f64()'.replace('.e+', '.0e') // JSON notation
if !val.contains('.') && val != '0' { // json notation if !val.contains('.') && val != '0' { // JSON notation
val += '.0' val += '.0'
} }
// Since https://github.com/vlang/v/pull/16079 V's string conversion of a zero (0) will
// output "0.0" for float types - the JSON test suite data, however, expects "0" for floats
// The following is a correction for that inconsistency
if val == '0.0' {
val = '0'
}
return '{ "type": "float", "value": "$val" }' return '{ "type": "float", "value": "$val" }'
} }
v := value.i64() v := value.i64()

View File

@ -1,3 +1,3 @@
[1, 0, 0] [1.0, 0.0, 0.0]
[0, 1, 0] [0.0, 1.0, 0.0]
[0, 0, 1] [0.0, 0.0, 1.0]

View File

@ -14,5 +14,5 @@ fn test_assign_map_value_of_fixed_array() {
arr = m['C'] arr = m['C']
println(arr) println(arr)
assert '$arr' == '[0, 0]' assert '$arr' == '[0.0, 0.0]'
} }

View File

@ -18,7 +18,7 @@ fn test_fixed_array_const_size() {
fn test_fixed_array_const_u64_size() { fn test_fixed_array_const_u64_size() {
a := [2 * u64_size]f64{} a := [2 * u64_size]f64{}
println(a) println(a)
assert '$a' == '[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]' assert '$a' == '[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]'
} }
const n = u64(5_000) const n = u64(5_000)

View File

@ -1,6 +1,6 @@
[vlib/v/tests/inout/dump_generic_interface_ref_arg.vv:30] mi.in_(): 1. [vlib/v/tests/inout/dump_generic_interface_ref_arg.vv:30] mi.in_(): 1.0
[vlib/v/tests/inout/dump_generic_interface_ref_arg.vv:31] mi.out(): 2. [vlib/v/tests/inout/dump_generic_interface_ref_arg.vv:31] mi.out(): 2.0
[vlib/v/tests/inout/dump_generic_interface_ref_arg.vv:36] in_put.in_(): 1. [vlib/v/tests/inout/dump_generic_interface_ref_arg.vv:36] in_put.in_(): 1.0
[vlib/v/tests/inout/dump_generic_interface_ref_arg.vv:37] in_put.out(): 2. [vlib/v/tests/inout/dump_generic_interface_ref_arg.vv:37] in_put.out(): 2.0
[vlib/v/tests/inout/dump_generic_interface_ref_arg.vv:39] in_put.in_(): 1. [vlib/v/tests/inout/dump_generic_interface_ref_arg.vv:39] in_put.in_(): 1.0
[vlib/v/tests/inout/dump_generic_interface_ref_arg.vv:40] in_put.out(): 2. [vlib/v/tests/inout/dump_generic_interface_ref_arg.vv:40] in_put.out(): 2.0

View File

@ -1,12 +1,12 @@
[vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:19] '$T.name $input': int 1 [vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:19] '$T.name $input': int 1
[vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:26] '$T.name $input': int 1 [vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:26] '$T.name $input': int 1
[vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:36] next(1): 0 [vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:36] next(1): 0.0
[vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:19] '$T.name $input': f64 1. [vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:19] '$T.name $input': f64 1.0
[vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:26] '$T.name $input': f64 1. [vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:26] '$T.name $input': f64 1.0
[vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:37] next(1.0): 64. [vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:37] next(1.0): 64.0
[vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:19] '$T.name $input': f64 11.1 [vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:19] '$T.name $input': f64 11.1
[vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:26] '$T.name $input': f64 11.1 [vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:26] '$T.name $input': f64 11.1
[vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:38] next(11.1): 64. [vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:38] next(11.1): 64.0
[vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:15] '$T.name $input': Score Score{ [vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.vv:15] '$T.name $input': Score Score{
ave: 23.4 ave: 23.4
} }

View File

@ -1,6 +1,6 @@
Point(Quad{ Point(Quad{
x: 1 x: 1.0
y: 2 y: 2.0
z: 3 z: 3.0
w: 1 w: 1.0
}) })

View File

@ -9,7 +9,7 @@ fn test_empty_interface_string_interpolation() {
b := IEmpty(f32(1)) b := IEmpty(f32(1))
c := IEmpty(Abc{'abc'}) c := IEmpty(Abc{'abc'})
assert '$a' == 'IEmpty(1)' assert '$a' == 'IEmpty(1)'
assert '$b' == 'IEmpty(1.)' assert '$b' == 'IEmpty(1.0)'
assert '$c'.starts_with('IEmpty(Abc{') assert '$c'.starts_with('IEmpty(Abc{')
assert '$c'.contains("x: 'abc'") assert '$c'.contains("x: 'abc'")
assert '$c'.ends_with('})') assert '$c'.ends_with('})')

View File

@ -13,15 +13,15 @@ fn test_variadic_array_decompose() {
a << Foo{} a << Foo{}
input := [0.0, 1.0] input := [0.0, 1.0]
assert a[0].method(...input) == '[0, 1]' assert a[0].method(...input) == '[0.0, 1.0]'
assert a[0].method(...[0.0, 1.0]) == '[0, 1]' assert a[0].method(...[0.0, 1.0]) == '[0.0, 1.0]'
} }
fn test_variadic_multiple_args() { fn test_variadic_multiple_args() {
mut a := []Element{} mut a := []Element{}
a << Foo{} a << Foo{}
assert a[0].method(0.0, 1.0) == '[0, 1]' assert a[0].method(0.0, 1.0) == '[0.0, 1.0]'
} }
interface Animal {} interface Animal {}

View File

@ -23,7 +23,7 @@ fn test_float_lit_call_method() {
x5 := 2.e-3.str() x5 := 2.e-3.str()
assert x5 == '0.002' assert x5 == '0.002'
x6 := 5.0.str() x6 := 5.0.str()
assert x6 == '5.' assert x6 == '5.0'
// x7 := 5..str() Syntax `5.` is allowed, but do not call method on it (`5..str()` is parsed as a range). Use `5.0.str()` instead. // x7 := 5..str() Syntax `5.` is allowed, but do not call method on it (`5..str()` is parsed as a range). Use `5.0.str()` instead.
x8 := 7.345e-7.str() x8 := 7.345e-7.str()
assert x8 == '7.345e-07' assert x8 == '7.345e-07'

View File

@ -1 +1 @@
0 0.0

View File

@ -30,7 +30,7 @@ fn test_fixed_array_struct_string_interpolation() {
ctx.vb = [1.1, x, 3.3, 4.4, 5.0, 6.0, 7.0, 8.9]! ctx.vb = [1.1, x, 3.3, 4.4, 5.0, 6.0, 7.0, 8.9]!
s := '$ctx' s := '$ctx'
assert s.starts_with('Context{') assert s.starts_with('Context{')
assert s.contains('vb: [1.1, 2.32, 3.3, 4.4, 5, 6, 7, 8.9]') assert s.contains('vb: [1.1, 2.32, 3.3, 4.4, 5.0, 6.0, 7.0, 8.9]')
assert s.ends_with('}') assert s.ends_with('}')
} }

View File

@ -76,7 +76,7 @@ fn test_typeof_on_sumtypes() {
assert typeof(c).name == 'MySumType' assert typeof(c).name == 'MySumType'
assert a.str() == '32' assert a.str() == '32'
assert b.str() == '123.' assert b.str() == '123.0'
assert c.str() == 'FooBar' assert c.str() == 'FooBar'
} }