18

While converting a float32 number to float64 precision is being lost in Go. For example converting 359.9 to float64 produces 359.8999938964844. If float32 can be stored precisely why is float64 losing precision?

Sample code:

package main

import (
    "fmt"
)

func main() {
    var a float32 = 359.9
    fmt.Println(a)
    fmt.Println(float64(a))
}

Try it on Playground

JJJ
  • 32,902
  • 20
  • 89
  • 102
Mayank Patel
  • 8,088
  • 5
  • 55
  • 75
  • 2
    https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html – Mysticial Sep 22 '16 at 15:28
  • 3
    This is bit me when I needed to use rationals. The extra precision "gained" converting to float64 gives a different numerator & denominator that can't be represented by int32. Ironically, this worked: `strconv.ParseFloat(fmt.Sprint(f32var), 64)` – Andrew Jun 04 '20 at 07:41

3 Answers3

18

You never lose precision when converting from a float (i.e. float32) to a double (float64). The former must be a subset of the latter.

It's more to do with the defaulting precision of the output formatter.

The nearest IEEE754 float to 359.9 is

359.899993896484375

The nearest IEEE754 double to 359.9 is

359.8999999999999772626324556767940521240234375

The nearest IEEE754 double to 359.899993896484375 is

359.899993896484375

(i.e. is the same; due to the subsetting rule I've already mentioned).

So you can see that float64(a) is the same as float64(359.899993896484375) which is 359.899993896484375. This explains that output, although your formatter is rounding off the final 2 digits.

Plato
  • 10,812
  • 2
  • 41
  • 61
  • I am talking about golang in particular here. golang can store 359.9 exactly but after converting to float64 value changes to 359.8999938964844. – Mayank Patel Sep 22 '16 at 15:21
  • I put a new sentence at the end that explains that,. –  Sep 22 '16 at 15:21
  • 1
    @MayankPatel: look at the output from the various floating point format verbs: https://play.golang.org/p/9CDBLy7DMQ – JimB Sep 22 '16 at 15:26
  • @jimb I don't think this is output formatter issue. Assuming output formatter print integer precisely. How do we explain this: https://play.golang.org/p/ydPa4234rp – Mayank Patel Sep 22 '16 at 15:44
  • 2
    and here I was thinking this answer is perfect. Can you not see it's due to converting 359.9 to a float which is 359.899993896484375, converting that to float64 has no effect, and you merely print that? –  Sep 22 '16 at 15:47
  • If converting 359.9 to a float produces 359.899993896484375, multiplying that with 100 and converting the result to int will produce 35989. Right? – Mayank Patel Sep 22 '16 at 15:58
  • Not true. You'll recover 35990 bang on. –  Sep 22 '16 at 16:03
  • 1
    @MayankPatel: no, because floating point numbers aren't decimal numbers. You're multiplying the numbers within the precision of 32 bit floats `(1.405859351158142x2^8) * (1.5625x2^6)` – JimB Sep 22 '16 at 16:03
  • 1
    To verify there is no loss of precision, convert the double back to float and print it as float. – Patricia Shanahan Sep 22 '16 at 21:26
6

This helped me understand @FaceyMcFaceFace's answer:

var a float32 = math.Pi
fmt.Println(a)
fmt.Println(float64(a))
fmt.Println(float64(math.Pi))

3.1415927
3.1415927410125732
3.141592653589793

https://play.golang.org/p/-bAQLqjlLG

Plato
  • 10,812
  • 2
  • 41
  • 61
1

I experienced the same problem and I am not satisfied with the accepted answer, because it doesn't really explain the float conversion behaviour in Go. Here is an example code that puzzles me and it shows that there's something strange going on when converting float32 to float64 in Golang. I would be very grateful if someone explains this in more detail.

package main

import (
    "fmt"
)

func main() {
    var f32 float32 = 0.2
    var f64 float64 = 0.2

    if float64(f32) == f64 { // this is false
        fmt.Println("Check succeeded")
    } else {
        fmt.Println("Check failed")
    }

    if float32(f64) == f32 { // this is true
        fmt.Println("Check succeeded")
    } else {
        fmt.Println("Check failed")
    }

    if float64(float32(f64)) == f64 { // this is false
        fmt.Println("Check succeeded")
    } else {
        fmt.Println("Check failed")
    }
}

https://play.golang.org/p/k2ctx4Zfehy

If the numbers become different after conversion, isn't it a loss of precision? Or maybe it should be called some other term, but this is definitely not an expected behaviour which may cause lots of nasty bugs and confusion. I faced this while unit-testing my application where a third-party library was doing a conversion from float32 to float64. I cannot properly explain this, but what I learned here is that you have to be very careful with converting floats and you shouldn't do it unless you really need to =)

Yaronius
  • 784
  • 3
  • 17
  • 34
  • 1
    Maybe the confusing thing is that this code is doing five conversions, not three. The decimal literal "0.2" cannot be represented exactly in binary floating point since it is not a finite sum of powers of two. Those first two assignments to f32 and f64 are storing the closest approximations to "0.2", but the 32-bit one is less accurate than the 64-bit one. So `f32` and `f64` really are different numbers right away. Another value like "0.25" (which is 2^-2) can be exactly represented in both float32 and float64, and all conditions would evaluate true. – Brian Hawkins Aug 03 '22 at 18:31