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/gc: inaccurate ideal float comparison #2789

Closed
gopherbot opened this issue Jan 26, 2012 · 7 comments
Closed

cmd/gc: inaccurate ideal float comparison #2789

gopherbot opened this issue Jan 26, 2012 · 7 comments
Milestone

Comments

@gopherbot
Copy link

by fan.howard:

package main
import "fmt"
func main(){
    fmt.Println(0.1+0.2)
    fmt.Println(0.1+0.2 >= 0.3)
    fmt.Println(0.1+0.2 < 0.3)
    i := 0.1
    fmt.Println(i+0.2)
    fmt.Println(i+0.2 >= 0.3)
    fmt.Println(i+0.2 < 0.3)

}

result:

0.3
false
true
0.30000000000000004
true
false

go version weekly.2012-01-15 11253

same on http://play.golang.org/
@peterGo
Copy link
Contributor

peterGo commented Jan 26, 2012

Comment 1:

"Numeric constants represent values of arbitrary precision and do not overflow."
http://golang.org/doc/go_spec.html#Constants
The compiler evaluates (0.1+0.2) using arbitrary precision arithmetic and then converts
the result to IEEE 754 64-bit floating-point arithmetic. At runtime, the executable
program evaluates (i+0.2) using IEEE 754 64-bit floating-point arithmetic.
package main
import (
    "fmt"
    "math"
)
func main() {
    i := 0.1
    fmt.Println((i + 0.2) == (0.1 + 0.2))
    fmt.Println(math.Float64bits(i+0.2), math.Float64bits(0.1+0.2))
}
Output:
false
4599075939470750516 4599075939470750515

@rsc
Copy link
Contributor

rsc commented Jan 26, 2012

Comment 2:

A simpler case is:
package main
const b = 0.1+0.2 < 0.3
func main() {
    println(b)
}
I would like to understand whether the compiler's internal
representation is making a mistake here or this is just an
inevitable rounding issue.  It seems like we should be able
to do better than this.
Remy, are you interested in this?
Russ

Labels changed: added priority-go1, removed priority-triage.

Owner changed to @rsc.

Status changed to Accepted.

@remyoudompheng
Copy link
Contributor

Comment 3:

We should probably clarify the meaning of ideal constants in the spec before. Depending
on the choice, there will be natural compiler representations of these:
1. "A numeric constant represent arbitrary precision integers. A conforming compiler may
use rounded representations for non-integral constants. The chosen representation must
at least represent exactly binary floating-point values with twice the bit count of
machine types."
In 1., I suppose the current implementation is conformant. It uses binary
floating-point, with multi-precision mantissa, and rounds a/b at 128 bits. With this
spec, the result of "0.1 + 0.2 == 0.3" is undefined, and "0.1 * 10 == 1" is allowed to
be always false. What does the current spec says about (1.0e999 - (1.0e999-1)) ? 
2. "A numeric constant represent arbitrary precision integers and decimal numbers. It
must exactly represent floating-point literals, however a conformant compiler may choose
to round the results of division of such constants".
In 2., the natural representation would be a multiprecision mantissa and a base 10
exponent in a reasonable range. Then "0.1 + 0.2 == 0.3" would be somewhat guaranteed to
be true, but "(1.0 / 49.0) * 49.0" is not guaranteed.
3. "A numeric constant represent exactly arbitrary precision integers, floating-point
literals as well as the result of operations between them."
In 3. the appropriate representation would probably be rational numbers, something like
struct Mprat { Mpint numerator, denominator; };
I think 3 is too much. 2 is reasonable but would require quite many changes to
compilers, and 1 is reasonable if the spec is clarified.

@rsc
Copy link
Contributor

rsc commented Jan 26, 2012

Comment 4:

It is too late to change the language spec at this point.
#1 is a good summary of what the spec says (or what we
mean it to say).
I understand that it is possible that 0.1 + 0.2 != 0.3 according
to the spec; however, I would like to understand if it is truly
an implication of using the numbers that gc does (they are
something like 700-bit precision) or just a bug in gc's rounding.
There have been significant bugs (millions of ULPs) in gc's
rounding before.
I just remembered that the raw constants can be read out of
the .6 files, and in this case it is working correctly, so it's
not a gc bug (hence closing as Unfortunate).
Remy, if you believe the spec is unclear as far as #1,
could you send a CL clarifying the language?
Thanks.
Russ

Status changed to Unfortunate.

@remyoudompheng
Copy link
Contributor

Comment 5:

I guess (but can't really tell right now) the rounding is correct. Maybe a workaround
would be to use an intermediate precision when it comes to ideal floatingpoint
comparison? It wouldn't eliminate the problem but maybe give less surprising results.
For example, in mpcmpfltflt we could say ideal equality would be defined as "truncations
to some number of bits, maybe half the total chosen precision, are equal".

@ianlancetaylor
Copy link
Contributor

Comment 6:

For what it's worth, gccgo gets this "right," in that it returns the results one would
naively expect.  gccgo represents ideal floating point constants using 128 bit precision.

@ianlancetaylor
Copy link
Contributor

Comment 7:

This issue was closed by revision 9126c65.

Status changed to Fixed.

@rsc rsc added this to the Go1 milestone Apr 10, 2015
@rsc rsc removed the priority-go1 label Apr 10, 2015
@golang golang locked and limited conversation to collaborators Jun 24, 2016
@rsc rsc removed their assignment Jun 22, 2022
This issue was closed.
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

5 participants