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 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图
  • ¥15 关于大棚监测的pcb板设计
  • ¥15 stm32开发clion时遇到的编译问题
  • ¥15 lna设计 源简并电感型共源放大器
  • ¥15 如何用Labview在myRIO上做LCD显示?(语言-开发语言)
  • ¥15 Vue3地图和异步函数使用
  • ¥15 C++ yoloV5改写遇到的问题
  • ¥20 win11修改中文用户名路径
  • ¥15 win2012磁盘空间不足,c盘正常,d盘无法写入