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

go/constant: possible regression in MakeFromLiteral #13719

Closed
kostya-sh opened this issue Dec 23, 2015 · 2 comments
Closed

go/constant: possible regression in MakeFromLiteral #13719

kostya-sh opened this issue Dec 23, 2015 · 2 comments
Milestone

Comments

@kostya-sh
Copy link
Contributor

The following program https://play.golang.org/p/Wov5d__elo prints

  • 3 with Go 1.5 (in the playground)
  • 4 with Go tip.
@kostya-sh
Copy link
Contributor Author

Unrelated but it might be good idea to add func (k Kind) String() method.

@ianlancetaylor ianlancetaylor added this to the Go1.6 milestone Dec 23, 2015
@griesemer
Copy link
Contributor

Working as intended. See below for details.

This is a somewhat unfortunate consequence of the fix for #11327; see the CL description for detailed explanation of the semantics change: d0c1746 .

Because now go/constant may use floating-point numbers (math/big.Float) to represent floating-point constants (necessary to be able to represent pathologically large constants such as 1e1000000 as required by the language spec), go/constant cannot always normalize a value to its smallest representation. For instance, in this case 2.0 was created from a float literal and thus has Kind Float even though (again in this specific case) it might be normalizable to Kind Int. Hence the difference of the Kind value (3 before, for Int; 4 now, for Float).

Instead, now, if we want to use a constant.Value as an integer, we must explicitly call constant.ToInt which will attempt to convert the value to Int Kind (and may fail). The reason this "normalization" cannot happen automatically is that it introduces loss: Some floating-point values that arise during constant computation may indeed be integers; and if evaluated exactly (as was done until recently when only rational arithmetic was used) this would be obvious. But with floating-point arithmetic, which may introduce errors (if ever so tiny), such a constant may not be an integer (i.e., Float.IsInt will return false for that value). The gc compiler (cmd/compile) and now also go/constant will allow a small error and still consider such a float an integer, if - and that is a big if - the value is used in conjunction with other integers; i.e., if ToInt is called explicitly. In other words, the compiler and go/constant assume such tiny errors are indeed just floating-point errors, and not errors in the source code.

If we would attempt normalization always (equivalent to calling ToInt after each operation and check if a value normalized to Int kind), we might incorrectly change floating-point numbers by possibly rounding them to integers, even if they were never supposed to be used as integers, and thus introduce loss incorrectly.

(There's possibly a way to stick to the old semantics of go/constant, which is changing the spec so it doesn't require to support insanely small or large floating-point numbers: see issue #13572. With the proposed smaller exponent range, even the largest - and likely pathological - values could probably be represented accurately as rationals with "reasonable" amounts of memory. Rational arithmetic is always mathematically exact, and constant arithmetic will never introduce an error at compile time.)

@golang golang locked and limited conversation to collaborators Dec 29, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants