Golang ParseFloat not accurate in example

I've been working on a project where I have to convert a string to a uint, to make sure some money values are matching:

total, err := strconv.ParseFloat(paymentResp.Transactions[0].Amount.Total, 64)
if err != nil {
    return ctx.JSON(http.StatusBadRequest, err.Error())
}

if o.TotalPrice != uint(total*100) {
    return ctx.JSON(http.StatusBadRequest, "Unable to verify amount paid")
}

But I've seemingly found a problem when trying to do the strconv.ParseFloat() on a couple of numbers, then attempting to multiply them by 100 (to get the cents value).

I've created an example here: Go Playground

f, _ := strconv.ParseFloat("79.35", 64)
fmt.Println(uint(f*100))  //7934

f2, _ := strconv.ParseFloat("149.20", 64)
fmt.Println(uint(f2*100)) //14919

Is ParseFloat() what I should be using in this scenario? If not, I'd love to hear a brief explanation on this, as I'm still a programmer in learning.

2 answers

  • answered 2018-08-09 02:25 whitespace

    you can split the string, and parse them to an int.

    package main
    
    import (
        "fmt"
        "strconv"
        "strings"
        )
    
    func main() {
        a:=strings.Split("75.35",".")
        if len(a)>2{
        panic("badly formatted price")    
         }
        v,_:=strconv.ParseInt(a[1],10,64)
        w,_:=strconv.ParseInt(a[0],10,64)
       fmt.Println(uint(w*100+v))
    }
    

    its working in this link https://play.golang.org/p/5s_FTAKSo9M

  • answered 2018-08-09 12:10 peterSO

    Go uses IEEE-754 binary floating-point numbers. Floating-point numbers are imprecise. Don't use them for financial transactions. Use integers.

    For example,

    package main
    
    import (
        "fmt"
        "strconv"
        "strings"
    )
    
    func parseCents(s string) (int64, error) {
        n := strings.SplitN(s, ".", 3)
        if len(n) != 2 || len(n[1]) != 2 {
            err := fmt.Errorf("format error: %s", s)
            return 0, err
        }
        d, err := strconv.ParseInt(n[0], 10, 56)
        if err != nil {
            return 0, err
        }
        c, err := strconv.ParseUint(n[1], 10, 8)
        if err != nil {
            return 0, err
        }
        if d < 0 {
            c = -c
        }
        return d*100 + int64(c), nil
    }
    
    func main() {
        s := "79.35"
        fmt.Println(parseCents(s))
        s = "149.20"
        fmt.Println(parseCents(s))
        s = "-149.20"
        fmt.Println(parseCents(s))
        s = "149.2"
        fmt.Println(parseCents(s))
    }
    

    Playground: https://play.golang.org/p/mGuO51QWyIv

    Output:

    7935 <nil>
    14920 <nil>
    -14920 <nil>
    0 format error: 149.2