How can we truncate float64 type to a particular precision?

前端 未结 12 1411
礼貌的吻别
礼貌的吻别 2021-02-02 05:33
package main

import (
    \"fmt\"
    \"strconv\"
    )

func main() {
    k := 10/3.0
    i := fmt.Sprintf(\"%.2f\", k)
    f,_ := strconv.ParseFloat(i, 2)
    fmt.Pri         


        
相关标签:
12条回答
  • 2021-02-02 05:56

    modify from @creack

    package main
    
    import (
        "fmt"
    )
    
    func main() {
    
        //untruncated := 10/3.0
        untruncated := 4.565
        tmp := int(untruncated*100)
        last := int(untruncated*1000)-tmp*10
        if last>=5{
            tmp += 1
        }
        truncated := float64(tmp)/100
    
        fmt.Println(untruncated, truncated)
    }
    
    0 讨论(0)
  • 2021-02-02 06:01

    I really don't see the point, but you could do something like this without strconv:

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        untruncated := 10 / 3.0
        truncated := float64(int(untruncated * 100)) / 100
        fmt.Println(untruncated, truncated)
    }
    
    0 讨论(0)
  • 2021-02-02 06:01
    func FloatPrecision(num float64, precision int) float64 {
        p := math.Pow10(precision)
        value := float64(int(num*p)) / p
        return value
    }
    
    0 讨论(0)
  • 2021-02-02 06:05

    The simplest solution is numeric truncation (assuming i is a float and you want a precision of 2 decimal points):

    float64(int(i * 100)) / 100
    

    For example:

    i := 123456.789
    x := float64(int(i * 100)) / 100
    // x = 123456.78
    

    BEWARE!

    If you're dealing with large numbers (numbers that can cross the max value boundaries), you should know that the above can lead to serious floating point accuracy issues:

    i := float64(1<<63) // 9223372036854775808.0
    fmt.Println(i, float64(int64(i * 10)) / 10)
    

    Prints: 9.223372036854776e+18 -9.223372036854776e+17

    See also:

    1. how 32 bit floating point numbers work
    2. how 64 bit floating point numbers work
    3. golang math numeric value range constants
    4. golang math/big
    0 讨论(0)
  • 2021-02-02 06:08

    The answer by threeve brought me to this issue on GitHub where a solution based on math/big for rounding values is presented - this way the rounding method is used correctly:

    package main
    
    import (
        "fmt"
        "math/big"
    )
    
    func main() {
        f := new(big.Float).SetMode(big.ToNearestEven).SetFloat64(10/3.0)
        // Round to 2 digits using formatting.
        f.SetPrec(8)
        fmt.Printf("%.2f\n", f)
    }
    

    The rounding mode is also respected in threeve's example:

    j := 0.045
    
    f := new(big.Float).SetMode(big.AwayFromZero).SetFloat64(j)
    // Round to 2 digits using formatting.
    f.SetPrec(8)
    fmt.Printf("%.2f\n", f)
    

    -> correctly yields 0.05

    Also, Go 1.10 has been released and added a math.Round() function, see this excellent answer by icza: Golang Round to Nearest 0.05

    package main
    
    import (
        "fmt"
        "math"
    )
    
    func main() {
    
        fmt.Println(Round(10/3.0, 0.01))
    
    }
    
    func Round(x, unit float64) float64 {
        return math.Round(x/unit) * unit
    }
    

    However, one should not use float for storing monetary values. (See: Why not use Double or Float to represent currency?) One way around this is using a library that implements decimal like https://github.com/ericlagergren/decimal or https://github.com/shopspring/decimal

    0 讨论(0)
  • 2021-02-02 06:11

    No one has mentioned using math/big. The results as pertains to the original question are the same as the accepted answer, but if you are working with floats that require a degree of precision ($money$), then you should use big.Float.

    Per the original question:

    package main
    
    import (
        "math/big"
        "fmt"
    )
    
    func main() {
        // original question
        k := 10 / 3.0
        fmt.Println(big.NewFloat(k).Text('f', 2))
    }
    

    Unfortunately, you can see that .Text does not use the defined rounding mode (otherwise this answer might be more useful), but rather always seems to round toward zero:

    j := 0.045
    fmt.Println(big.NewFloat(j).SetMode(big.AwayFromZero).Text('f', 2)
    
    // out -> 0.04
    

    Nevertheless, there are certain advantages to having your float stored as a big.Float.

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