6

I'm new to Golang. I wanted to separate a floating point number into whole and decimal parts. After some research I implemented it but there is a problem in my code. I'm using 5.8 as the input but the result is 5 and 0.79999.

package main

import(
        "fmt"
        "math"
)    

func Round2(val float64) {
        intpart, div := math.Modf(val)
        fmt.Println(div)

        fmt.Println(intpart)

}

func main() {
        fmt.Println("Hello, playground")
        Round2(5.8)
}

I have tried this, I'm getting an output of:

0.7999999999999998
5

If there is any other way to do this please let me know. I have inserted the go playground with my code in it.

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

jasonlam604
  • 1,456
  • 2
  • 16
  • 25
Rajesh Kumar
  • 207
  • 1
  • 6
  • 12
  • It's a precision error, but it also depends on how you're formatting the output: https://play.golang.org/p/eFmkI8x3tO – JimB Oct 20 '16 at 15:08
  • 1
    Use `math.Modf()` as explained in this answer https://stackoverflow.com/questions/16534820/how-to-test-whether-a-float-is-a-whole-number-in-go – DeNix Mar 12 '20 at 23:18

4 Answers4

4

This is an artifact of floating-point arithmetic. A floating-point value is not always exactly the value you expect it to be due to the fact that not all numbers can be represented exactly in the floating-point format used by whatever processor you happen to be using. Obviously, the value is very close to 0.8 and is equal to 15 significant figures. Note that this is not specific to Go.

Instead of

fmt.Println(div)

try

fmt.Printf(".4f\n", div) // shows only 4 decimal places

It looks like this article has some good information: https://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

Andy Schweig
  • 6,597
  • 2
  • 16
  • 22
1

Try this:

f := 5.8
ipart := int64(f)
fmt.Println(ipart)
decpart := fmt.Sprintf("%.7g", f-float64(ipart))[2:]
fmt.Println(decpart)

The complete answer in available in go playground. Basically I used the format specifier %g to achieve the result, moreover, %.6g rounds the decimal part to six places, trailing zeroes are not shown.

Documentation of fmt package

blmayer
  • 300
  • 4
  • 15
0

You may want to use a Decimal64p2 type of decimal with fixed .00 precision - https://github.com/strongo/decimal

It is efficient for storing exact amounts of money.

Alexander Trakhimenok
  • 6,019
  • 2
  • 27
  • 52
0

In my case, I needed the exact decimal digits and Modf was changing some digits. So, I have created a utility function like this:

func getIntDecimal(val float64) (int64, float64, error) {
  decimalPart := 0.00
  s := fmt.Sprintf("%v", val)
  split := strings.Split(s, ".")
  decimalStr := "0." + split[1]
  decimalPart, err := strconv.ParseFloat(decimalStr, 4)
  intPart, err := strconv.ParseInt(split[0], 10, 0)
  fmt.Println(intPart, decimalPart)
  return intPart, decimalPart, err
}

Use it like this in your main function:

v := 252.4569
i, d, err := getIntDecimal(v)
if err != nil {
    log.Printf("Error %s in converting value to int and decimal", err)
}
fmt.Println(i, f)

And the output would be:

252 0.4569
hiteshpariyani
  • 371
  • 3
  • 5