Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

encoding/json: float32 may not convert correctly #34559

Closed
c-yan opened this issue Sep 26, 2019 · 4 comments
Closed

encoding/json: float32 may not convert correctly #34559

c-yan opened this issue Sep 26, 2019 · 4 comments

Comments

@c-yan
Copy link

@c-yan c-yan commented Sep 26, 2019

What version of Go are you using (go version)?

>go version
go version go1.13.1 windows/amd64

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

go env Output
>go env
set GO111MODULE=
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\c-yan\AppData\Local\go-build
set GOENV=C:\Users\c-yan\AppData\Roaming\go\env
set GOEXE=.exe
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\c-yan\go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=c:\go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=c:\go\pkg\tool\windows_amd64
set GCCGO=gccgo
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Users\c-yan\AppData\Local\Temp\2\go-build235639566=/tmp/go-build -gno-record-gcc-switches

What did you do?

I converted a float32 value to JSON and the value went differnt.
https://play.golang.org/p/_giUH9Yq35f

What did you expect to see?

Expected to get 2684354560, the same value as before conversion.

What did you see instead?

I got 2684354600 which is different from before conversion. Since fmt.Printf can display correctly, I think it is a issue of encoding/json. If we do roughly the same thing in C#, GSon(Java), node.js, there is no issue.

This issue was first reported here.

@hasitpbhatt

This comment has been minimized.

Copy link
Contributor

@hasitpbhatt hasitpbhatt commented Sep 26, 2019

Actually, strconv.AppendFloat converts it like this, due to which json.Marshal is also returning an incorrect value.

Same can be confirmed in https://play.golang.org/p/JroJ5RijGFx

@Nathan-Fenner

This comment has been minimized.

Copy link

@Nathan-Fenner Nathan-Fenner commented Sep 26, 2019

This is not a bug. There's no way to distinguish float32(2684354560) and float32(2684354600) since they're exactly the same value. Go has to choose some serialization for them (which must be the same, since, again, they're the same value), and it picks one that as as-few-as-possible significant digits.

@rsc

This comment has been minimized.

Copy link
Contributor

@rsc rsc commented Sep 26, 2019

This is not a bug. Here's what happens with float64s in JavaScript:

> JSON.stringify(123456789123456789123)
"123456789123456800000"

This behavior is generalized to float32s for Go's JSON parser: print as few non-zero digits as is needed to precisely identify the right float32. Note that reading the value back as a float32 reproduces the original float32. In float32 math, 2684354600 == 2684354560.

Similarly, in float64 math, 123456789123456789123 == 123456789123456800000.

If you want to distinguish between 2684354600 and 2684354560, the answer is to not use a float32 at all. Use a float64 instead. (In general a float32 has so little precision that it's almost never the right answer.)

I'd be happy to look at examples from other languages but almost all of them are probably operating on float64s, not float32s, either because the language doesn't have float32 at all or because the language does the C-like thing where float32s become float64s in most calls.

That is, try:

	var f32 float32 = 2684354560
	j, _ := json.Marshal(float64(f32)) // float64 conversion here!
	fmt.Println(string(j))
	fmt.Printf("%.0f", f32)

That's what most C-like languages would do implicitly.

@rsc rsc closed this Sep 26, 2019
@c-yan

This comment has been minimized.

Copy link
Author

@c-yan c-yan commented Sep 26, 2019

I verified 2684354432 to 2684354688 have the same value.
https://play.golang.org/p/0ZzhuUmTkE2

Excuse me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.