首先复习一下基础,golang中的数字类型有整型int,浮点型float
整型
int分为无符号,有符号俩种,具体又分类为8,32,64等
package main
import (
"fmt"
"math"
)
func main() {
// uint 最大值
var maxUInt int = math.MaxUint8
fmt.Printf("uint 最大值: %d\n", maxUInt)
// int 最大值和最小值
var maxInt int = math.MaxInt
var minInt int = math.MinInt
fmt.Printf("int 最大值: %d\n", maxInt)
fmt.Printf("int 最小值: %d\n", minInt)
// int32 最大值和最小值
var maxInt32 int32 = math.MaxInt32
var minInt32 int32 = math.MinInt32
fmt.Printf("int32 最大值: %d\n", maxInt32)
fmt.Printf("int32 最小值: %d\n", minInt32)
// int64 最大值和最小值
var maxInt64 int64 = math.MaxInt64
var minInt64 int64 = math.MinInt64
fmt.Printf("int64 最大值: %d\n", maxInt64)
fmt.Printf("int64 最小值: %d\n", minInt64)
}
uint 最大值: 255
int 最大值: 9223372036854775807
int 最小值: -9223372036854775808
int32 最大值: 2147483647 即 2^31−1
int32 最小值: -2147483648 即 -2^31
int64 最大值: 9223372036854775807
int64 最小值: -9223372036854775808
浮点型
float分为单精度float32和双精度float64
实际范围是基于IEEE 754标准的单、双精度浮点数表示
IEEE754 浮点数:简读+案例=秒懂_ieee754浮点数的计算
float的弊端
1. 精度问题
package main
import "fmt"
func main() {
a := 0.1
b := 0.2
sum := a + b
fmt.Println(sum) // 输出 0.30000000000000004 而不是 0.3
}
0.30000000000000004
2. 舍入问题
package main
import (
"fmt"
)
func main() {
a := 1.0000001
b := 1.0000002
difference := a - b
fmt.Println(difference) // 预期输出 0.0000001
}
-9.999999983634211e-08
3. 比较问题
package main
import (
"fmt"
)
func main() {
a := float64(0.1) + float64(0.2)
b := float64(0.3)
fmt.Println(a == b)
}
false
4. 累计误差
package main
import (
"fmt"
)
func main() {
a := float64(0.1)
for i := 0; i < 10; i++ {
a += float64(0.1)
}
fmt.Println(a)
}
1.0999999999999999
如何解决上述问题,引出下文 如何计算金额
金额如何计算
我们的要求是计算时不损失精度的加法、减法、乘法,除法指定精度
库: github.com/shopspring/decimal
原理
数据结构
// number = value * 10 ^ exp
type Decimal struct {
value *big.Int
exp int32
}
type Int struct {
neg bool // sign
abs nat // absolute value of the integer
}
type nat []Word
type Word uint
加法
转换为 value * 10 ^ exp形式的计数法,然后底层使用SDK的大数加减法进行计算
x + y == x + y
(-x) + (-y) == -(x + y)
x + (-y) == x - y == -(y - x)
(-x) + y == y - x == -(x - y)
z.neg = len(z.abs) > 0 && neg // 0 has no sign
func addVV(z, x, y []Word) (c Word)
func addVW(z, x []Word, y Word) (c Word)
func subVV(z, x, y []Word) (c Word)
func subVW(z, x []Word, y Word) (c Word)
减法
x - (-y) == x + y
(-x) - y == -(x + y)
x - y == x - y == -(y - x)
(-x) - (-y) == y - x == -(x - y)
乘法
x * y == x * y
x * (-y) == -(x * y)
(-x) * y == -(x * y)
(-x) * (-y) == x * y
如果2个数的最小长度小于40位使用基础乘法
func addMulVVW(z, x []Word, y Word) (c Word)
除法
// div returns q, r such that q = ⌊u/v⌋ and r = u%v = u - q·v.
func (z nat) div(z2, u, v nat) (q, r nat)
拓展1: 一块钱分给3个人怎么分
惯性思维 1/3=0.33 0.33*3 = 0.99 剩下0.01消失了
正确操作 2个人拿走: 1/3=0.33 最后一个人拿走 1-0.33*(2)=0.34
拓展2: 淘宝的满减券怎么一回事
淘宝上满减后退货退的金额怎么算?退款会退到哪里?
举个例子:
满减券在淘宝中是按照比例进行抵扣的,例如有一张满300减50的券。购买第一件商品100元,第二件商品200元。
那么实际核销时会显示
第一件商品抵扣50*(100/(100+200))=16.66
第二件商品抵扣50-16.66=33.34
n件商品依此类推 附图