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

cmd/compile: missing truncation error #20750

Closed
josharian opened this issue Jun 22, 2017 · 2 comments

Comments

Projects
None yet
4 participants
@josharian
Copy link
Contributor

commented Jun 22, 2017

package p

var _ = 2 + 1/1E500 | "5"[0]

gotype says:

x.go:3:9: 2 + 1 / 1E500 (untyped float constant 2) truncated to byte

cmd/compile (1.7, 1.8, 1.9beta1) accepts the code.

Found with go-fuzz. cc @dvyukov @griesemer

@josharian josharian added this to the Go1.10 milestone Jun 22, 2017

@ALTree

This comment has been minimized.

Copy link
Member

commented Jun 22, 2017

Simpler

package p

var _ = int(2 + 1/1E500)

cmd/compile eats it without complaining (and it evaluates to 2), go/types says

cannot convert 2 + 1 / 1E500 (untyped float constant 2) to int

2 + 1 / 1E500 evaluates to an untyped 2 (because the internal gc prec limit does not allow the compiler to manage the value 2 + 1 / 1E500). That untyped 2 is representable as an int, so the conversion succeeds. Why does go/types reject it?

Spec says every implementation must:

Round to the nearest representable constant if unable to represent a floating-point or complex constant due to limits on precision.

Maybe the go/types internal precision is just higher than the gc one, and this is not a bug at all, since both behaviours are permitted by the spec. It just happens that go/types has enough precision to represent 2 + 1/1e500 without rounding to 2, and then it correctly rejects the conversion to int since the argument is not an integer (while in gc it is).

@griesemer

This comment has been minimized.

Copy link
Contributor

commented Jun 22, 2017

@ALTree is correct. Per the spec (https://tip.golang.org/ref/spec#Constants):

Implementation restriction: Although numeric constants have arbitrary precision in the language, a compiler may implement them using an internal representation with limited precision. That said, every implementation must:

  • Represent integer constants with at least 256 bits.
  • Represent floating-point constants, including the parts of a complex constant, with a mantissa of at least 256 bits and a signed binary exponent of at least 16 bits.
  • Give an error if unable to represent an integer constant precisely.
  • Give an error if unable to represent a floating-point or complex constant due to overflow.
  • Round to the nearest representable constant if unable to represent a floating-point or complex constant due to limits on precision

1/1e500 (or 1e-500) for that matter can be represented as non-integer (non-zero) floating-point number with 256 bits of mantissa and 16 bits of exponent, but 1 + 1e-500 evaluates to 2.0 after rounding (not enough mantissa bits), which is why the compiler accepts it (actually, the compiler even accepts a small error around 2.0 as 2 because otherwise a lot of operations we'd take for granted wouldn't work at all.

go/types uses rational numbers for constants and doesn't round (the result is always exact *). Thus, 1 + 1e-500 is clearly not an integer.

(* There's an escape hatch where even go/types moves to a big.Float representation in situations where the rational number components become so large as to make it practically impossible for the compiler to finish constant arithmetic in a reasonable amount of time, but that only kicks in in extreme cases).

Closing as implementation restriction.

@griesemer griesemer closed this Jun 22, 2017

@golang golang locked and limited conversation to collaborators Jun 22, 2018

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
You can’t perform that action at this time.