Re: [golang-dev] Confused about the float32 type

521 views
Skip to first unread message

Brad Fitzpatrick

unread,
Aug 26, 2015, 12:56:23 PM8/26/15
to Timothy Ye, golang-nuts
[-golang-dev, +golang-nuts]

You can read more about floating point numbers here:

and



On Wed, Aug 26, 2015 at 7:35 AM, Timothy Ye <yexiaoz...@gmail.com> wrote:
Hey guys, 

I met a weird problem about the float32 type in Go.

This is my demo program:  (You can run it via: http://play.golang.org/p/dn4V4TcFtr)

package main

import "fmt"

func main() {
compute(4.8)
compute(4.7)
compute(4.6)
compute(4.5)
}

func compute(input float32) {
result := int64(input * 100)
fmt.Printf("Result is:%d\r\n", result)
result = int64(int(input *10)*10)
fmt.Printf("Result is:%d\r\n", result)
}

==================

The output is:

Result is:480
Result is:480
Result is:469
Result is:470
Result is:460
Result is:460
Result is:450
Result is:450

You may notice that all the results are as expected except when input is 4.7
I am confused about the float 4.7 * 100, why the result is 469?

But if I let 4.7 * 10 first, then convert it to int type and multiply by 10 again, the result is 470, not 469.

I don't know why, can anybody help to explain it? Thanks a lot!

--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Timothy Ye

unread,
Aug 26, 2015, 1:27:26 PM8/26/15
to golang-nuts, yexiaoz...@gmail.com
Thanks for your reply. It is caused by the precision of float32 type.

Another question is how do I get 470, not 469 if I really have to let 4.7 * 100?

I mean is there a best practice to handle it?


在 2015年8月27日星期四 UTC+8上午12:56:23,bradfitz写道:

Ian Lance Taylor

unread,
Aug 26, 2015, 1:28:44 PM8/26/15
to Timothy Ye, golang-nuts
On Wed, Aug 26, 2015 at 10:27 AM, Timothy Ye <yexiaoz...@gmail.com> wrote:
> Thanks for your reply. It is caused by the precision of float32 type.
>
> Another question is how do I get 470, not 469 if I really have to let 4.7 *
> 100?
>
> I mean is there a best practice to handle it?

Don't use floating point?

What are you really trying to do?

Ian

Timothy Ye

unread,
Aug 26, 2015, 9:57:29 PM8/26/15
to golang-nuts, yexiaoz...@gmail.com
Hi Ian,

Actually, 4.7 is the price of one product, 100 is the amount user ordered.
I expect to get total price 470, not 469...     -_-#


在 2015年8月27日星期四 UTC+8上午1:28:44,Ian Lance Taylor写道:

Steven Blenkinsop

unread,
Aug 26, 2015, 11:32:17 PM8/26/15
to Timothy Ye, golang-nuts
Yeah, floating point is not good for financial computations. Computing costs in cents using integers could work, depending on your rounding strategy.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Wilhelmina Drengwitz

unread,
Aug 26, 2015, 11:41:23 PM8/26/15
to Timothy Ye, golang-nuts
You may want look into a fixed point decimal package, such as decnum[1].

See this issue[2] for other potential (non)fixed point decimal
packages to try out.

[1] https://godoc.org/github.com/rin01/decnum
[2] https://github.com/golang/go/issues/12332
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.

dave.t...@gmail.com

unread,
Aug 26, 2015, 11:42:17 PM8/26/15
to golang-nuts, yexiaoz...@gmail.com
As others have noted, you should use fixed-point numbers for prices or transactions in practice.   That said, int conversion here will always round down - it's a truncation.   You may want to round before truncating - use (x - math.Floor()), compare the result to 0.5, and adjust up as necessary...

xiio...@gmail.com

unread,
Aug 27, 2015, 7:45:15 AM8/27/15
to golang-nuts, yexiaoz...@gmail.com
package main
import "fmt"
func main() {
 compute(4.8)
 compute(4.7)
 compute(4.6)
 compute(4.5)
}
func compute(input float32) {
 fmt.Println(input * 100)
}
 

This shows explicity what is going wrong. As noted above there are other ways to round (https://en.wikipedia.org/wiki/Rounding#Rounding_to_integer)

If working with currency use ints - with the lowest denomination as the base value (ie cents). For interest calculations on currency there's still a use for Binary Coded Decimal to avoid errors on 1/10ths 1/100ths etc - but this is a technical subject I'm not qualified to fully discuss.

Russ Cox

unread,
Aug 27, 2015, 11:14:11 AM8/27/15
to Timothy Ye, golang-nuts
On Wed, Aug 26, 2015 at 1:27 PM, Timothy Ye <yexiaoz...@gmail.com> wrote:
Thanks for your reply. It is caused by the precision of float32 type.
Another question is how do I get 470, not 469 if I really have to let 4.7 * 100? 
I mean is there a best practice to handle it?

Abandoning floating point is the usual response to this kind of question, but it's really not necessary here. As others have pointed out, floating point numbers cannot represent decimal numbers exactly. There is a tiny bit of error due to rounding to a representable value. But that by itself does not explain your program's behavior.

For a float32, the representation error is not more than 1 part in 2²⁴, or 0.000006%. 
For a float64, the representation error is not more than 1 part in 2⁵³, or 0.00000000000001%.

In your program, however, the error in your computation is 1 part in 470, or 0.2%. This is a good sign that the problem is not due to floating point representation error. Instead, the problem is just the conversion from float32 to int. In Go, like in C and other languages, the conversion from a floating point number to an integer discards the fractional part; that is, it truncates toward zero. The closest float32 representation for 4.7 is actually 4.69999980926513671875; multiplying that by 100 gives the float32 469.999969482421875. Converting that to int with a Go conversion discards the fractional part, leaving 469.

The way to keep tiny errors from becoming large errors during the conversion is to make the conversion round to the nearest integer instead of discarding the fractional part. The idiom for that is to use int(x+0.5) instead of int(x). If you make that simple change in your program, everything will work the way you expect. See http://play.golang.org/p/MVvelzhHVu, which prints:

x = float32(4.7) = 4.69999980926513671875
x*100 = 469.999969482421875
int(x*100) = 469
int(x*100+0.5) = 470

It is worth a note of caution about float32. Although float32 is good enough for this particular computation, its range is very limited. That 0.000006% means that for a given decimal number, a float32 only stores the first 7 digits accurately. In contrast, a float64 stores 15 digits accurately. In US dollars, assuming you care about pennies, that means a float32 fails to store $200,000.01 precisely while a float64 can precisely represent the current US national debt ($18,154,615,041,694.74). Unless you are squeezing space and know exactly what the tradeoff is that you're making, you should almost always use float64, not float32.

Assuming you do use float64, floating point is perfectly fine for the vast majority of financial computations, and the various fixed point decimal or floating decimal packages are overkill. (In fact, again for most computations, a bug in the implementation of one of those seems more likely to me than an error caused by float64 precision.) Just make sure you round correctly when converting to integer.

Russ
Reply all
Reply to author
Forward
0 new messages