四舍五入详解(这不是你认知的四舍五入)
很详细,有很多Demo并提供了在线运行地址。想搞明白得自己多试试
三种规则
- 标准四舍五入
- 四舍五基础上正负不一样
- 银行家算法
一、标准四舍五入
代表工具
描述
和名字一样,就是我们大部分人认知的四舍五入
例子
10.5 ==> 11
-10.5 ==> -11
10.885 ==> 10.89 (保留两位)
-10.885 ==> -10.89 (保留两位)
实操
go在线运行地址(需要翻墙)
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
var f = []float64{10.5, -10.5, 10.885, -10.885}
for _, v := range f {
// decimal库
fmt.Println(v, "==> decimal库", decimal.NewFromFloat(v).StringFixed(2))
fmt.Println("--------")
}
}
二、四舍五入基础上正负不一样
代表工具
- Math.round()
- numbro库(v2.3.1版本前)通过浏览器端运行第三方库的插件将numbro库装到浏览器环境中即可运行
描述
- 正数按照严标准的四舍五入来
- 负数四舍六入,五后大于0则入,五后为0就舍
例子
10.5 ==> 11
-10.5 ==> -10
-10.51 ==> -11
实操
const nums = [10.5, -10.5, -10.51];
const test = () => {
nums.forEach(item => console.log(`${item} ==> Math.round ${Math.round(item)}`));
};
test();
const nums = [10.5, -10.5, 10.885, -10.885, -10.8851];
const test = () => {
nums.forEach(item => console.log(`${item} ==> numbro库 ${numbro(item).format('0.00')}`));
};
test();
三、银行家算法
代表工具
- java可配置使用
- C#的进位,VB.net
- 很多博客上说 toFixed 和 Go 自带的也是(我表示怀疑,起码官方没说,详情见下方标题四的内容)
描述
- 四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一
- 银行家舍入是IEEE规定的小数舍入标准之一,也是IEEE目前规定中最优秀的舍入方法,因此所有符合 IEEE 标准的语言都应该实现这种算法
例子
9.8249 ==> 9.82(四舍)
9.82671 ==> 9.83(六入)
9.8351 ==> 9.84(五后非零就进一)
9.82501 ==> 9.83(五后非零就进一)
9.8250 ==> 9.82(五后为零看奇偶,五前为偶应舍去)
9.8350 ==> 9.84(五后为零看奇偶,五前为奇要进一)
实操
自己有兴趣可以试试,我没在线运行试,不是搞后端的
四、toFixed 和 Go自带的fmt
描述
哪个都不是,没找到规律,可能是银行家算法,但是由于浮点数不能精确地用二进制表示所有小数。这可能会导致意外的结果
结论
经尝试 toFixed 和 go 是一样的,从结果来看不是银行家算法,但可能设计的时候是银行家算法,但是由于浮点数不能精确地用二进制表示所有小数而产生了意外。
toFixed
实操
toFixed 在线运行地址 结论并不是5后为0看奇偶
//程序运行完成时一定要有输出语句,本工具才能正确展示运行结果。
const list = [989.075,989.175,989.275,989.375,989.475,989.575,989.675,989.775,989.875,989.975];
const list1 = [989.065,989.165,989.265,989.365,989.465,989.565,989.665,989.765,989.865,989.965];
const yhj = [9.8249, 9.82671, 9.8351, 9.82501, 9.8250, 9.8350];
list.forEach((item)=>{console.log(`${item} ==> ${item.toFixed(2)}`)})
console.log('--------------')
list1.forEach((item)=>{console.log(`${item} ==> ${item.toFixed(2)}`)})
console.log('--------------')
//银行家算法:四舍六入,五后不为0则如,为零看五前,前一个奇数进位,偶数舍去
console.log('银行家算法:四舍六入,五后不为0则如,为零看五前,前一个奇数进位,偶数舍去');
yhj.forEach((item)=>{console.log(`${item} ==> ${item.toFixed(2)}`)})
Go的fmt
实操
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
var f = []float64{989.075, 989.175, 989.275, 989.375, 989.475, 989.575, 989.675, 989.775, 989.875, 989.975}
var f1 = []float64{989.065, 989.165, 989.265, 989.365, 989.465, 989.565, 989.665, 989.765, 989.865, 989.965}
var yhj = []float64{9.8249, 9.82671, 9.8351, 9.82501, 9.8250, 9.8350}
for _, v := range f {
fmt.Print(v,"==>",decimal.NewFromFloat(v).StringFixed(2))
fmt.Printf(" go自带的fmt %0.2f\n", v)
}
fmt.Println("******************************")
for _, v1 := range f1 {
fmt.Print(v1,"==> decimal库",decimal.NewFromFloat(v1).StringFixed(2) )
fmt.Printf(" go自带的fmt %0.2f\n", v1)
}
fmt.Println("******************************")
for _, v2 := range yhj{
fmt.Print(v2,"==>",decimal.NewFromFloat(v2).StringFixed(2))
fmt.Printf(" go自带的fmt %0.2f\n", v2)
}
}