Counterintuitive floating point behaviour in simple case

140 views
Skip to first unread message

Tom Payne

unread,
Oct 11, 2013, 8:01:27 AM10/11/13
to golang-nuts
Hi,

I was surprised to find that in Go, given:
a := 6.0 + float64(28792)/60000.0
b := 6.0 + 28792.0/60000.0
that a != b.

The only difference in the above two lines is the typecast: float64(28792) vs. the numeric constant 28792.0.

Even more strangely, if you remove the "6.0 +" then the two values *are* equal:

I've looked at the Go specification for constants and constant expressions, 

Is this because of the way the compiler represents floating point constants with greater precision than float64?

Cheers,
Tom

Jan Mercl

unread,
Oct 11, 2013, 8:23:59 AM10/11/13
to Tom Payne, golang-nuts
On Fri, Oct 11, 2013 at 2:01 PM, Tom Payne <twp...@gmail.com> wrote:

http://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 exponent
of at least 32 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.

These requirements apply both to literal constants and to the result
of evaluating constant expressions.
""""

-j

Ian Lance Taylor

unread,
Oct 11, 2013, 9:47:55 AM10/11/13
to Tom Payne, golang-nuts
Yes. If you use math.Float64bits you'll see that they differ in one
ULP.

The computation float64(28792)/60000.0 is not using untyped constant.
The conversion to float64 causes the division to be done in as
float64. The result is different from the division done as an untyped
constant, because the untyped constants have higher precision.

Ian

Tom Payne

unread,
Oct 11, 2013, 9:47:56 AM10/11/13
to Jan Mercl, golang-nuts
Thanks Jan. I'd read the part of the spec that you cite, but I did not understand what exactly is happening here.

Is the presence of a typecast causing one constant to be evaluated with 64-bit floats and the other (without the typecast) to be evaluated with higher precision (e.g. the minim 256 bit mantissa in the spec)?

Cheers,
Tom

Tom Payne

unread,
Oct 11, 2013, 10:12:33 AM10/11/13
to Ian Lance Taylor, golang-nuts
Thanks Ian.

For information, I stumbled across this while writing tests for some floating point code. To get floating point equality (so that I could use reflect.DeepEqual), I found I had to use the typecasts so that expected values matched the values returned. I'll write a version of reflect.DeepEqual that handles small floating point differences now, which is probably the best way to solve the problem.

Regards,
Tom
Reply all
Reply to author
Forward
0 new messages