我正在寻找一个在Golang中舍入到最接近的0.05的函数.使用该函数的最终结果必须始终为0.05.
以下是我正在寻找的函数的一些输出示例:(函数Round尚不存在,我希望它可以包含在答案中)
Round(0.363636) // 0.35
Round(3.232) // 3.25
Round(0.4888) // 0.5
Run Code Online (Sandbox Code Playgroud)
我现在已经搜索了很多年,并没有找到任何答案.
icz*_*cza 85
Go 1.10已经发布,它增加了一个math.Round()
功能.此函数舍入到最接近的整数(基本上是"舍入到最接近的1.0"操作),使用它我们可以很容易地构造一个舍入到我们选择的单位的函数:
func Round(x, unit float64) float64 {
return math.Round(x/unit) * unit
}
Run Code Online (Sandbox Code Playgroud)
测试它:
fmt.Println(Round(0.363636, 0.05)) // 0.35
fmt.Println(Round(3.232, 0.05)) // 3.25
fmt.Println(Round(0.4888, 0.05)) // 0.5
fmt.Println(Round(-0.363636, 0.05)) // -0.35
fmt.Println(Round(-3.232, 0.05)) // -3.25
fmt.Println(Round(-0.4888, 0.05)) // -0.5
Run Code Online (Sandbox Code Playgroud)
在Go Playground尝试一下.
原始答案是在Go 1.10之前创建的,当时不math.Round()
存在,并且还详细说明了我们的自定义Round()
函数背后的逻辑.它出于教育目的.
在Go1.10之前的时代,没有math.Round()
.但...
舍入任务可以通过float64
=> int64
converison 轻松实现,但必须小心,因为float到int转换不是舍入而是保留整数部分(请参阅Go中的详细信息:使用乘法器将float64转换为int).
例如:
var f float64
f = 12.3
fmt.Println(int64(f)) // 12
f = 12.6
fmt.Println(int64(f)) // 12
Run Code Online (Sandbox Code Playgroud)
结果是12
在两种情况下,整数部分.要获得舍入"功能",只需添加0.5
:
f = 12.3
fmt.Println(int64(f + 0.5)) // 12
f = 12.6
fmt.Println(int64(f + 0.5)) // 13
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.但我们不想舍入到整数.如果我们想要舍入到1分数,我们会在添加0.5
和转换之前乘以10 :
f = 12.31
fmt.Println(float64(int64(f*10+0.5)) / 10) // 12.3
f = 12.66
fmt.Println(float64(int64(f*10+0.5)) / 10) // 12.7
Run Code Online (Sandbox Code Playgroud)
所以基本上你乘以你想要舍入的单位的倒数.要舍入到0.05
单位,乘以1/0.05 = 20
:
f = 12.31
fmt.Println(float64(int64(f*20+0.5)) / 20) // 12.3
f = 12.66
fmt.Println(float64(int64(f*20+0.5)) / 20) // 12.65
Run Code Online (Sandbox Code Playgroud)
将其包装成一个函数:
func Round(x, unit float64) float64 {
return float64(int64(x/unit+0.5)) * unit
}
Run Code Online (Sandbox Code Playgroud)
使用它:
fmt.Println(Round(0.363636, 0.05)) // 0.35
fmt.Println(Round(3.232, 0.05)) // 3.25
fmt.Println(Round(0.4888, 0.05)) // 0.5
Run Code Online (Sandbox Code Playgroud)
试试Go Playground上的例子.
需要注意的是四舍五入3.232
有unit=0.05
不准确打印3.25
,但0.35000000000000003
.这是因为float64
数字是使用有限精度存储的,称为IEEE-754标准.有关详细信息,请参阅Golang将float64转换为int错误.
另请注意,unit
可能是"任意"号码.如果是1
,则Round()
基本上舍入到最接近的整数.如果它是10
,它会舍入到数十,如果是0.01
,它会舍入到2个小数位.
另请注意,当您Round()
使用负数进行呼叫时,您可能会得到令人惊讶的结果:
fmt.Println(Round(-0.363636, 0.05)) // -0.3
fmt.Println(Round(-3.232, 0.05)) // -3.2
fmt.Println(Round(-0.4888, 0.05)) // -0.45
Run Code Online (Sandbox Code Playgroud)
这是因为所述-as转换earlier-是保持整数部分,以及用于例如整数部分-1.6
是-1
(其大于-1.6
;而整数部分1.6
是1
小于1.6
).
如果你想-0.363636
成为-0.35
而不是-0.30
,那么在负数的情况下添加-0.5
而不是0.5
在Round()
函数内部.看我们改进的Round2()
功能:
func Round2(x, unit float64) float64 {
if x > 0 {
return float64(int64(x/unit+0.5)) * unit
}
return float64(int64(x/unit-0.5)) * unit
}
Run Code Online (Sandbox Code Playgroud)
并使用它:
fmt.Println(Round2(-0.363636, 0.05)) // -0.35
fmt.Println(Round2(-3.232, 0.05)) // -3.25
fmt.Println(Round2(-0.4888, 0.05)) // -0.5
Run Code Online (Sandbox Code Playgroud)
编辑:
为了解决你的评论:因为你没有"喜欢"不完全的评论0.35000000000000003
,你建议格式化它并重新解析它:
formatted, err := strconv.ParseFloat(fmt.Sprintf("%.2f", rounded), 64)
Run Code Online (Sandbox Code Playgroud)
并且这种"看似"会产生确切的结果,因为它0.35
完全打印出来.
但这只是一种"错觉".由于0.35
无法使用IEEE-754标准用有限位表示,无论你对数字做什么都没关系,如果你把它存储在一个类型的值中float64
,它就不会完全正确0.35
(但IEEE-754数字非常接近)它).你看到的是fmt.Println()
打印它,0.35
因为fmt.Println()
已经做了一些舍入.
但如果您尝试以更高的精度打印它:
fmt.Printf("%.30f\n", Round(0.363636, 0.05))
fmt.Printf("%.30f\n", Round(3.232, 0.05))
fmt.Printf("%.30f\n", Round(0.4888, 0.05))
Run Code Online (Sandbox Code Playgroud)
输出:它不是更好(可能更丑):在Go Playground尝试:
0.349999999999999977795539507497
3.250000000000000000000000000000
0.500000000000000000000000000000
Run Code Online (Sandbox Code Playgroud)
另一方面请注意3.25
并且0.5
它们是精确的,因为它们可以用有限位完全表示,因为以二进制表示:
3.25 = 3 + 0.25 = 11.01binary
0.5 = 0.1binary
Run Code Online (Sandbox Code Playgroud)
这是什么教训?不值得格式化和重新解析结果,因为它也不准确(只是一个不同的float64
值 - 根据默认的fmt.Println()
格式规则 - 可能在打印时更好).如果你想要很好的打印格式,只需要精确格式化,如:
func main() {
fmt.Printf("%.3f\n", Round(0.363636, 0.05))
fmt.Printf("%.3f\n", Round(3.232, 0.05))
fmt.Printf("%.3f\n", Round(0.4888, 0.05))
}
func Round(x, unit float64) float64 {
return float64(int64(x/unit+0.5)) * unit
}
Run Code Online (Sandbox Code Playgroud)
它将是准确的(在Go Playground尝试):
0.350
3.250
0.500
Run Code Online (Sandbox Code Playgroud)
或者只是将它们相乘100
并使用整数,这样就不会出现任何表示或舍入错误.