nim-lang / Nim

Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula. Its design focuses on efficiency, expressiveness, and elegance (in that order of priority).
https://nim-lang.org
Other
16.59k stars 1.47k forks source link

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

Closed timotheecour closed 4 years ago

timotheecour commented 4 years ago

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 !

Example

echo 0.6

Current Output

0.59999999999999998

Expected Output

0.6

Additional Information

that PR aimed to fix https://github.com/nim-lang/Nim/issues/13196 but IMO there should be a better fix that doesn't introduce this regression. EDIT: indeed, see https://github.com/nim-lang/Nim/pull/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):

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)
```c++
#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;
}

consequences

shows as:

image

proposal

kaushalmodi commented 4 years ago

The amount of research you did for this regression issue is amazing! 👏

krux02 commented 4 years ago

[0.10000000000000001,0.20000000000000001,0.29999999999999999,0.40000000000000002,0.5,0.59999999999999998,0.69999999999999996,0.80000000000000004,0.90000000000000002]

this will blow up size of json files

Correction, this will blow up size for json, if the value comes from a handwritten decimal number. To my experience the majority of json number data doesn't come from floating point numbers entered manually in decimal.

this may break other people's tests that relied on stringifications of simple looking numbers

True. And I am sorry for that.

Regarding the output of [0.10000000000000001,0.20000000000000001,0.29999999999999999,0.40000000000000002,0.5,0.59999999999999998,0.69999999999999996,0.80000000000000004,0.90000000000000002] instead of [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9], I personally think this is an improvement. It is a very common mistake from beginners to think 0.1 * 10 == 1, which is not the case. When echo 0.1 does print 0.10000000000000001 instead of 0.1 it might be clear that 0.1 * 10 isn't 1. In other words I think printing 0.1 as 0.1 precisely is a lie from the language, because the internal value isn't 0.1 precisely.

regarding printf("%g\n", a), this one doesn't survive the roundtrip.

#include <math.h>
#include <stdio.h>

int main() {
  double f = 0.1;
  for(int i = 0; i < 100; ++i) {
    printf("%g\n", f);
    f = nextafter(f, 100);
  }
}

output: only 0.1, nothing else. same with C++


#include <cmath>
#include <iostream>

using namespace std;

int main() {
  double f = 0.1;
  for(int i = 0; i < 100; ++i) {
    cout << f << endl;
    f = nextafter(f, 100);
  }
}
timotheecour commented 4 years ago

I personally think this is an improvement. It is a very common mistake from beginners to think 0.1 * 10 == 1, which is not the case

beginners need to learn about FP semantics. Just like in any language.

regarding printf("%g\n", a), this one doesn't survive the roundtrip.

there's no roundtrip here. A language's default echo command shouldn't be used for serialization; the roundtrip is for serialization (such as json/protobuf etc). In any case https://github.com/nim-lang/Nim/issues/13365 (ryu) is the way forward, and would give you the "best of both worlds":

since #13276 has been reverted, I'm gonna close this issue; followup work is to implement https://github.com/nim-lang/Nim/issues/13365

krux02 commented 4 years ago

beginners need to learn about FP semantics. Just like in any language.

That works best if the language is honest about FP

there's no roundtrip here.

Whatever...