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

如何将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 VMBox虚拟机无法访问
  • ¥15 skd显示找不到头文件
  • ¥15 机器视觉中图片中长度与真实长度的关系
  • ¥15 R语言卸载之后无法重装,显示电脑存在下载某些较大二进制文件行为,怎么办
  • ¥15 java 的protected权限 ,问题在注释里
  • ¥15 这个是哪里有问题啊?
  • ¥15 关于#vue.js#的问题:修改用户信息功能图片无法回显,数据库中只存了一张图片(相关搜索:字符串)
  • ¥15 texstudio的问题,
  • ¥15 spaceclaim模型变灰色
  • ¥15 求一份华为esight平台V300R009C00SPC200这个型号的api接口文档