doupai8095 2019-01-25 09:52
浏览 187
已采纳

如何将float64数字截断为特定精度?

I want to truncate 1.234567 into a 3-fraction digit floating point number, but the result is not what I want.

E.g: 1.234567 => 1.234

package main

import (
    "strconv"
    "fmt"
)

func main() {
    f := 1.234567
    fmt.Println(strconv.FormatFloat(f, 'f', 3, 64)) //1.235
    fmt.Printf("%.3f", f) //1.235
}

Can anyone tell me how to do this in Go?

  • 写回答

2条回答 默认 最新

  • dsoy71058 2019-01-28 09:55
    关注

    The naive way (not always correct)

    For truncation, we could take advantage of math.Trunc() which throws away the fraction digits. This is not exactly what we want, we want to keep some fraction digits. So in order to achieve what we want, we may first multiply the input by a power of 10 to shift the wanted fraction digits to the "integer" part, and after truncation (calling math.Trunc() which will throw away the remaining fraction digits), we can divide by the same power of 10 we multiplied in the beginning:

    f2 := math.Trunc(f*1000) / 1000
    

    Wrapping this into a function:

    func truncateNaive(f float64, unit float64) float64 {
        return math.Trunc(f/unit) * unit
    }
    

    Testing it:

    f := 1.234567
    f2 := truncateNaive(f, 0.001)
    fmt.Printf("%.10f
    ", f2)
    

    Output:

    1.2340000000
    

    So far so good, but note that we perform arithmetic operations inside truncateNaive() which may result in unwanted roundings, which could alter the output of the function.

    For example, if the input is 0.299999999999999988897769753748434595763683319091796875 (it's representable by a float64 value exactly, see proof), the output should be 0.2999000000, but it will be something else:

    f = 0.299999999999999988897769753748434595763683319091796875
    f2 = truncateNaive(f, 0.001)
    fmt.Printf("%.10f
    ", f2)
    

    Output:

    0.3000000000
    

    Try these on the Go Playground.

    This wrong output is probably not acceptable in most cases (except if you look at it from a way that the input is very close to 0.3–difference is less than 10-16–to which the output is 0.3...).

    Using big.Float

    To properly truncate all valid float64 values, the intermediate operations must be precise. To achieve that, using a single float64 is insufficient. There are ways to split the input into 2 float64 values and perform operations on them (so precision is not lost) which would be more efficient, or we could use a more convenient way, big.Float which can be of arbitrary precision.

    Here's the "transcript" of the above truncateNaive() function using big.Float:

    func truncate(f float64, unit float64) float64 {
        bf := big.NewFloat(0).SetPrec(1000).SetFloat64(f)
        bu := big.NewFloat(0).SetPrec(1000).SetFloat64(unit)
    
        bf.Quo(bf, bu)
    
        // Truncate:
        i := big.NewInt(0)
        bf.Int(i)
        bf.SetInt(i)
    
        f, _ = bf.Mul(bf, bu).Float64()
        return f
    }
    

    Testing it:

    f := 1.234567
    f2 := truncate(f, 0.001)
    fmt.Printf("%.10f
    ", f2)
    
    f = 0.299999999999999988897769753748434595763683319091796875
    f2 = truncate(f, 0.001)
    fmt.Printf("%.10f
    ", f2)
    

    Output is now valid (try it on the Go Playground):

    1.2340000000
    0.2990000000
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 求daily translation(DT)偏差订正方法的代码
  • ¥15 js调用html页面需要隐藏某个按钮
  • ¥15 ads仿真结果在圆图上是怎么读数的
  • ¥20 Cotex M3的调试和程序执行方式是什么样的?
  • ¥20 java项目连接sqlserver时报ssl相关错误
  • ¥15 一道python难题3
  • ¥15 牛顿斯科特系数表表示
  • ¥15 arduino 步进电机
  • ¥20 程序进入HardFault_Handler
  • ¥15 关于#python#的问题:自动化测试