Golang converting float64 to int error

前端 未结 1 775
旧时难觅i
旧时难觅i 2020-12-04 02:26

How can I avoid floating point errors when converting float\'s to int\'s. For example the following code prints: 0.5499999999999972 when I wound expect it to p

相关标签:
1条回答
  • 2020-12-04 03:26

    You need to understand something: 100.55 is a decimal number (presented in decimal radix). 100.55 in decimal is a finite number and is exactly this: 100.55.

    Computers in general store numbers in binary representation. The number 100.55 cannot be represented with a finite binary number: 100.55 is an infinite number in binary representation (same reason why 1/3 cannot be represented with a finite decimal number, it is an endless sequence: 0.333333333....).

    But Go (like any other language) stores float64 types using the IEEE-754 standard, which is a finite binary representation. A float64 value uses 64 bits in memory to describe the number, of which 53 bits are used to describe the digits and 11 bits are used for the exponent.

    Now when you "say" this:

    x := 100.55
    

    It is a short variable declaration which will create a new variable named x and infer its type from the right hand side expression which is a floating point literal, so by the Go spec x's type will be float64. The floating point literal will have to be "converted" in order to be represented using 64 bits (by rules specified by IEEE-754). And since 100.55 would require infinite bits to be represented precisely in binary radix, by using only 64 bits (53 for the digits) the result will not (cannot) be exactly 100.55 (but a 64-bit binary number in IEEE-754 format that is closest to it), which is:

    x := 100.55
    fmt.Printf("%.50f\n", x)
    
    100.54999999999999715782905695959925651550292968750000
    

    So you are already starting off with a number not being 100.55.

    You subtract 100 from it (float64(int(x)) will be exactly 100.0):

    x = x - float64(int(x))
    fmt.Printf("%.50f\n", x)
    
    0.54999999999999715782905695959925651550292968750000
    

    What can you do about it? Nothing really. The result you expect (0.55) is also an infinite number in binary representation, so you can't have an exact number of 0.55 in a variable of type float64.

    What you can do is work with the number as normal, but when you print it, round it to decimal places of your choice. The easiest is to use fmt.Printf(), and specify a format string using the verb %f including the precision:

    fmt.Printf("%.2f\n", x)
    

    Result:

    0.55
    

    Another option is to avoid using floating point numbers. E.g. if you were to represent USD amounts, you could "multiply" all your values by 100 and represent amounts as cents (1$*100) which is an integer. Only if you need to print the result as USD, you could print something like

    cents := 10055
    fmt.Printf("%d.%d $", cents/100, cents%100)
    

    Output:

    100.55 $
    
    0 讨论(0)
提交回复
热议问题