-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
-
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.6Current 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 jnow 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.6shows as:
- this affects the AST as well, eg:
import macros
macro dbg(a): untyped =
echo a.repr
echo a.treeRepr
dbg:
let a = 0.6now prints:
let a = 0.59999999999999998
StmtList
LetSection
IdentDefs
Ident "a"
Empty
FloatLit 0.59999999999999998proposal
- first we should revert printing float values will have one more digit. #13276
- then we should figure out a better solution to fix Lossy json serialization/deserialization round-trip for floats #13196 ; other languages make this work without affecting how litterals like 0.6 are printed, so there is a better solution out there for sure; EDIT: see properly fix #13196: json serialization with option for lossless roundtrip #13364
