From 43b9a716c584cc677feb2b24c0f2af4abeabd963 Mon Sep 17 00:00:00 2001 From: Subhomoy Haldar Date: Mon, 17 Oct 2022 13:41:07 +0100 Subject: [PATCH] builtin,strconv: append ".0", to float string representations, to ensure clarity (#16079) --- CHANGELOG.md | 1 + vlib/builtin/float.c.v | 16 +++--- vlib/builtin/map_of_floats_test.v | 4 +- vlib/builtin/string_int_test.v | 16 +++--- vlib/math/math_test.v | 2 +- vlib/strconv/f32_f64_to_string_test.v | 9 ++- vlib/strconv/utilities.c.v | 56 ++++++++++++------- vlib/toml/tests/burntsushi.toml-test_test.v | 10 +++- .../mutable_receiver_type_mapping.out | 6 +- .../assign_map_value_of_fixed_array_test.v | 2 +- vlib/v/tests/fixed_array_const_size_test.v | 2 +- .../inout/dump_generic_interface_ref_arg.out | 12 ++-- .../dump_nested_generic_fn_call_ref_arg.out | 10 ++-- .../tests/inout/printing_reference_alias.out | 8 +-- .../empty_interface_1_test.v | 2 +- vlib/v/tests/interface_variadic_test.v | 6 +- vlib/v/tests/num_lit_call_method_test.v | 2 +- .../tests/skip_unused/generics_method.run.out | 2 +- .../generics_method.skip_unused.run.out | 2 +- .../tests/string_interpolation_struct_test.v | 2 +- vlib/v/tests/typeof_test.v | 2 +- 21 files changed, 98 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d1d2bce93..b65549f50c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## V 0.3.2 *Not yet released* - 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 *31 Aug 2022* diff --git a/vlib/builtin/float.c.v b/vlib/builtin/float.c.v index d75278f98f..2a2481688a 100644 --- a/vlib/builtin/float.c.v +++ b/vlib/builtin/float.c.v @@ -19,10 +19,10 @@ pub fn (x f64) str() string { f: x } if f.u == strconv.double_minus_zero { - return '-0' + return '-0.0' } if f.u == strconv.double_plus_zero { - return '0' + return '0.0' } } abs_x := f64_abs(x) @@ -37,11 +37,11 @@ pub fn (x f64) str() string { [inline] pub fn (x f64) strg() string { if x == 0 { - return '0' + return '0.0' } abs_x := f64_abs(x) 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 { return strconv.ftoa_64(x) } @@ -85,10 +85,10 @@ pub fn (x f32) str() string { f: x } if f.u == strconv.single_minus_zero { - return '-0' + return '-0.' } if f.u == strconv.single_plus_zero { - return '0' + return '0.' } } abs_x := f32_abs(x) @@ -103,11 +103,11 @@ pub fn (x f32) str() string { [inline] pub fn (x f32) strg() string { if x == 0 { - return '0' + return '0.0' } abs_x := f32_abs(x) 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 { return strconv.ftoa_32(x) } diff --git a/vlib/builtin/map_of_floats_test.v b/vlib/builtin/map_of_floats_test.v index 7481107928..7f21a34cd8 100644 --- a/vlib/builtin/map_of_floats_test.v +++ b/vlib/builtin/map_of_floats_test.v @@ -2,7 +2,7 @@ fn test_map_of_f32() { mut m32 := map[f32]string{} m32[1.0] = 'one' println(m32) - assert '$m32' == r"{1.: 'one'}" + assert '$m32' == r"{1.0: 'one'}" for k, v in m32 { assert typeof(k).name == 'f32' assert typeof(v).name == 'string' @@ -17,7 +17,7 @@ fn test_map_of_f64() { } m64[1.0] = 'one' println(m64) - assert '$m64' == r"{3.14: 'pi', 1.: 'one'}" + assert '$m64' == r"{3.14: 'pi', 1.0: 'one'}" for k, v in m64 { assert typeof(k).name == 'f64' assert typeof(v).name == 'string' diff --git a/vlib/builtin/string_int_test.v b/vlib/builtin/string_int_test.v index ff45d3e6bf..b7b920e02d 100644 --- a/vlib/builtin/string_int_test.v +++ b/vlib/builtin/string_int_test.v @@ -190,11 +190,11 @@ fn test_signed_cast() { mut u := strconv.Float64u{ u: strconv.double_plus_zero } - assert '${u.f:g}' == '0' - assert '${u.f:G}' == '0' + assert '${u.f:g}' == '0.0' + assert '${u.f:G}' == '0.0' u.u = strconv.double_minus_zero - assert '${u.f:g}' == '0' - assert '${u.f:G}' == '0' + assert '${u.f:g}' == '0.0' + assert '${u.f:G}' == '0.0' u.u = strconv.double_plus_infinity assert '${u.f:g}' == '+inf' assert '${u.f:G}' == '+INF' @@ -206,11 +206,11 @@ fn test_signed_cast() { mut u := strconv.Float32u{ u: strconv.single_plus_zero } - assert '${u.f:g}' == '0' - assert '${u.f:G}' == '0' + assert '${u.f:g}' == '0.0' + assert '${u.f:G}' == '0.0' u.u = strconv.single_minus_zero - assert '${u.f:g}' == '0' - assert '${u.f:G}' == '0' + assert '${u.f:g}' == '0.0' + assert '${u.f:G}' == '0.0' u.u = strconv.single_plus_infinity assert '${u.f:g}' == '+inf' assert '${u.f:G}' == '+INF' diff --git a/vlib/math/math_test.v b/vlib/math/math_test.v index 93cf6792ef..0bbf21122b 100644 --- a/vlib/math/math_test.v +++ b/vlib/math/math_test.v @@ -425,7 +425,7 @@ fn test_abs_zero() { ret2 := abs(0.0) println(ret2) - assert '$ret2' == '0' + assert '$ret2' == '0.0' } fn test_floor() { diff --git a/vlib/strconv/f32_f64_to_string_test.v b/vlib/strconv/f32_f64_to_string_test.v index 0b24aa28f3..d73de5702e 100644 --- a/vlib/strconv/f32_f64_to_string_test.v +++ b/vlib/strconv/f32_f64_to_string_test.v @@ -138,7 +138,7 @@ fn test_float_to_str() { // test f32 for c, x in test_cases_f32 { - println(x) + // println(x) s := strconv.f32_to_str(x, 8) s1 := exp_result_f32[c] // println("$s1 $s") @@ -154,10 +154,13 @@ fn test_float_to_str() { } // 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()) // println(a) - assert a.len == exp + 1 + assert a.len == exp + 3 b := strconv.f64_to_str_l(('1e-' + exp.str()).f64()) // println(b) diff --git a/vlib/strconv/utilities.c.v b/vlib/strconv/utilities.c.v index d2c33a3a0f..095087aefe 100644 --- a/vlib/strconv/utilities.c.v +++ b/vlib/strconv/utilities.c.v @@ -36,14 +36,14 @@ pub fn f32_to_str_l(f f32) string { 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. -// The decimal digits after the dot can be omitted. +// f32_to_str_l_with_dot returns `f` as a `string` in decimal notation with a maximum of 6 digits after the dot. +// 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] -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) - res := fxx_to_str_l_parse_no_dot(s) + res := fxx_to_str_l_parse_with_dot(s) unsafe { s.free() } return res } @@ -59,14 +59,14 @@ pub fn f64_to_str_l(f f64) string { 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. -// The decimal digits after the dot can be omitted. +// f64_to_str_l_with_dot returns `f` as a `string` in decimal notation with a maximum of 18 digits after the dot. +// 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] -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) - res := fxx_to_str_l_parse_no_dot(s) + res := fxx_to_str_l_parse_with_dot(s) unsafe { s.free() } return res } @@ -187,23 +187,30 @@ pub fn fxx_to_str_l_parse(s string) string { i++ } } - /* - // remove the dot form the numbers like 2. - if r_i > 1 && res[r_i-1] == `.` { - r_i-- + + // Add a zero after the dot from the numbers like 2. + if r_i > 1 && res[r_i - 1] == `.` { + 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 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. -// 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] -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 if s.len > 2 && (s[0] == `n` || s[1] == `i`) { 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] == `.` { - 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 diff --git a/vlib/toml/tests/burntsushi.toml-test_test.v b/vlib/toml/tests/burntsushi.toml-test_test.v index 61a4e651f6..b066be6117 100644 --- a/vlib/toml/tests/burntsushi.toml-test_test.v +++ b/vlib/toml/tests/burntsushi.toml-test_test.v @@ -238,10 +238,16 @@ fn to_burntsushi(value ast.Value) string { } if !value.text.starts_with('0x') && (value.text.contains('.') || value.text.to_lower().contains('e')) { - mut val := '$value.f64()'.replace('.e+', '.0e') // json notation - if !val.contains('.') && val != '0' { // json notation + mut val := '$value.f64()'.replace('.e+', '.0e') // JSON notation + if !val.contains('.') && val != '0' { // JSON notation 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" }' } v := value.i64() diff --git a/vlib/v/gen/c/testdata/mutable_receiver_type_mapping.out b/vlib/v/gen/c/testdata/mutable_receiver_type_mapping.out index 94133394da..9718f7f76e 100644 --- a/vlib/v/gen/c/testdata/mutable_receiver_type_mapping.out +++ b/vlib/v/gen/c/testdata/mutable_receiver_type_mapping.out @@ -1,3 +1,3 @@ -[1, 0, 0] -[0, 1, 0] -[0, 0, 1] \ No newline at end of file +[1.0, 0.0, 0.0] +[0.0, 1.0, 0.0] +[0.0, 0.0, 1.0] \ No newline at end of file diff --git a/vlib/v/tests/assign_map_value_of_fixed_array_test.v b/vlib/v/tests/assign_map_value_of_fixed_array_test.v index d9568f1a0f..19ae86ae2f 100644 --- a/vlib/v/tests/assign_map_value_of_fixed_array_test.v +++ b/vlib/v/tests/assign_map_value_of_fixed_array_test.v @@ -14,5 +14,5 @@ fn test_assign_map_value_of_fixed_array() { arr = m['C'] println(arr) - assert '$arr' == '[0, 0]' + assert '$arr' == '[0.0, 0.0]' } diff --git a/vlib/v/tests/fixed_array_const_size_test.v b/vlib/v/tests/fixed_array_const_size_test.v index 8048eb38d7..988805747f 100644 --- a/vlib/v/tests/fixed_array_const_size_test.v +++ b/vlib/v/tests/fixed_array_const_size_test.v @@ -18,7 +18,7 @@ fn test_fixed_array_const_size() { fn test_fixed_array_const_u64_size() { a := [2 * u64_size]f64{} 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) diff --git a/vlib/v/tests/inout/dump_generic_interface_ref_arg.out b/vlib/v/tests/inout/dump_generic_interface_ref_arg.out index d5c99f92ad..c082f14e32 100644 --- a/vlib/v/tests/inout/dump_generic_interface_ref_arg.out +++ b/vlib/v/tests/inout/dump_generic_interface_ref_arg.out @@ -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:31] mi.out(): 2. -[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:37] in_put.out(): 2. -[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:40] in_put.out(): 2. +[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.0 +[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.0 +[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.0 diff --git a/vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.out b/vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.out index e333bf90f2..47ade6b921 100644 --- a/vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.out +++ b/vlib/v/tests/inout/dump_nested_generic_fn_call_ref_arg.out @@ -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: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:19] '$T.name $input': f64 1. -[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:37] next(1.0): 64. +[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.0 +[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.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: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{ ave: 23.4 } diff --git a/vlib/v/tests/inout/printing_reference_alias.out b/vlib/v/tests/inout/printing_reference_alias.out index 331fd4103f..da3e408fb1 100644 --- a/vlib/v/tests/inout/printing_reference_alias.out +++ b/vlib/v/tests/inout/printing_reference_alias.out @@ -1,6 +1,6 @@ Point(Quad{ - x: 1 - y: 2 - z: 3 - w: 1 + x: 1.0 + y: 2.0 + z: 3.0 + w: 1.0 }) diff --git a/vlib/v/tests/interface_edge_cases/empty_interface_1_test.v b/vlib/v/tests/interface_edge_cases/empty_interface_1_test.v index 160393c0f9..cba099610f 100644 --- a/vlib/v/tests/interface_edge_cases/empty_interface_1_test.v +++ b/vlib/v/tests/interface_edge_cases/empty_interface_1_test.v @@ -9,7 +9,7 @@ fn test_empty_interface_string_interpolation() { b := IEmpty(f32(1)) c := IEmpty(Abc{'abc'}) assert '$a' == 'IEmpty(1)' - assert '$b' == 'IEmpty(1.)' + assert '$b' == 'IEmpty(1.0)' assert '$c'.starts_with('IEmpty(Abc{') assert '$c'.contains("x: 'abc'") assert '$c'.ends_with('})') diff --git a/vlib/v/tests/interface_variadic_test.v b/vlib/v/tests/interface_variadic_test.v index 020af540e1..8d2617c58f 100644 --- a/vlib/v/tests/interface_variadic_test.v +++ b/vlib/v/tests/interface_variadic_test.v @@ -13,15 +13,15 @@ fn test_variadic_array_decompose() { a << Foo{} input := [0.0, 1.0] - assert a[0].method(...input) == '[0, 1]' - assert a[0].method(...[0.0, 1.0]) == '[0, 1]' + assert a[0].method(...input) == '[0.0, 1.0]' + assert a[0].method(...[0.0, 1.0]) == '[0.0, 1.0]' } fn test_variadic_multiple_args() { mut a := []Element{} 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 {} diff --git a/vlib/v/tests/num_lit_call_method_test.v b/vlib/v/tests/num_lit_call_method_test.v index 448911295b..754e080d79 100644 --- a/vlib/v/tests/num_lit_call_method_test.v +++ b/vlib/v/tests/num_lit_call_method_test.v @@ -23,7 +23,7 @@ fn test_float_lit_call_method() { x5 := 2.e-3.str() assert x5 == '0.002' 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. x8 := 7.345e-7.str() assert x8 == '7.345e-07' diff --git a/vlib/v/tests/skip_unused/generics_method.run.out b/vlib/v/tests/skip_unused/generics_method.run.out index 573541ac97..ba66466c2a 100644 --- a/vlib/v/tests/skip_unused/generics_method.run.out +++ b/vlib/v/tests/skip_unused/generics_method.run.out @@ -1 +1 @@ -0 +0.0 diff --git a/vlib/v/tests/skip_unused/generics_method.skip_unused.run.out b/vlib/v/tests/skip_unused/generics_method.skip_unused.run.out index 573541ac97..ba66466c2a 100644 --- a/vlib/v/tests/skip_unused/generics_method.skip_unused.run.out +++ b/vlib/v/tests/skip_unused/generics_method.skip_unused.run.out @@ -1 +1 @@ -0 +0.0 diff --git a/vlib/v/tests/string_interpolation_struct_test.v b/vlib/v/tests/string_interpolation_struct_test.v index cda7d2bbf5..aa5f253a29 100644 --- a/vlib/v/tests/string_interpolation_struct_test.v +++ b/vlib/v/tests/string_interpolation_struct_test.v @@ -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]! s := '$ctx' 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('}') } diff --git a/vlib/v/tests/typeof_test.v b/vlib/v/tests/typeof_test.v index d0a9586e0b..286b64c4bd 100644 --- a/vlib/v/tests/typeof_test.v +++ b/vlib/v/tests/typeof_test.v @@ -76,7 +76,7 @@ fn test_typeof_on_sumtypes() { assert typeof(c).name == 'MySumType' assert a.str() == '32' - assert b.str() == '123.' + assert b.str() == '123.0' assert c.str() == 'FooBar' }