Skip to content

regression caused by PR #13276: echo 0.6 show 0.59999999999999998, unlike all other languages #13362

@timotheecour

Description

@timotheecour
  • the recently merged printing float values will have one more digit. #13276 introduced the regression /cc @krux02

  • that PR commented out some tests and changed some numbers for preexisting tests in tests/system/tostring.nim to deal with the fact that numbers now print weirdly; this will likely break other people's tests in the wild

  • pretty much no other language I know of does that, and for good reason:

python, D, C, C++, go, node js, matlab, octave, java...

yet they can handle serialization/deserialization roundtrip just fine, and aren't any less correct; printing 0.6 as 0.6 does not imply any less precision !

  • this is bad for interop with other languages; eg it can blow up size of json messages, etc; it affects AST rendering, doc generation and countless other things

Example

echo 0.6

Current Output

0.59999999999999998

Expected Output

0.6

Additional Information

that PR aimed to fix #13196 but IMO there should be a better fix that doesn't introduce this regression. EDIT: indeed, see #13364

Indeed, other languages I've tried don't have that serialization/deserialization round-trip issue;

and pretty much all languages I'm familiar with print float literals like 0.4 as simply 0.4 (using default printing command):

  • python
a=0.4
print(a) # 0.4
assert(str(a) == '0.4')
import json
x = 0.12345678901234567890123456789
json.loads(json.dumps(x)) == x
  • D
import std.stdio;
import std.json;
import std.conv;

void main(){
  auto a = 0.4;
  writeln(a);
  assert(a.to!string == "0.4");
  auto x = 0.12345678901234567890123456789;
  auto x2 = parseJSON(JSONValue(x).to!string).floating();
  assert(x2 == x);
}
  • C and C++ (I only checked printing, not json serialization/deserialization)
#include <iostream>
#include <stdio.h>
int main (int argc, char *argv[]) {
  double a = 0.4;
  std::cout << a << std::endl; // (C++, C) prints as 0.4
  printf("%g\n", a); // (C) prints as 0.4
  return 0;
}
  • go
package main
import "fmt"
func main() {
	var a = 0.4
	fmt.Println(a) # prints 0.4
}
  • matlab / octave: ditto
  • node js: ditto

consequences

  • this may break other people's tests that relied on stringifications of simple looking numbers like echo (0.4, "foo")

  • this will blow up size of json files which impacts performance etc

  import std/json
  var s = @[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
  echo s
  var j = %* s
  echo j

now shows:

@[0.10000000000000001, 0.20000000000000001, 0.29999999999999999, 0.40000000000000002, 0.5, 0.59999999999999998, 0.69999999999999996, 0.80000000000000004, 0.90000000000000002]
[0.10000000000000001,0.20000000000000001,0.29999999999999999,0.40000000000000002,0.5,0.59999999999999998,0.69999999999999996,0.80000000000000004,0.90000000000000002]
  • this affects nim doc runnableExamples, eg:
  proc bar*()=
    runnableExamples:
      var x = 0.6
      doAssert x == 0.6

shows as:

image

  • this affects the AST as well, eg:
  import macros
  macro dbg(a): untyped =
    echo a.repr
    echo a.treeRepr
  dbg:
    let a = 0.6

now prints:

let a = 0.59999999999999998
StmtList
  LetSection
    IdentDefs
      Ident "a"
      Empty
      FloatLit 0.59999999999999998

proposal

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions