我的服务以字符串形式接收价格,例如“ 12.34”。我必须将其保存为美分(db),因此必须为1234。以下解决方案安全吗?安全意味着结果始终相同且正确。 “ 12.34”必须为1234,但不能为1235或1233。
s := "12.34"
f, _ := strconv.ParseFloat(s, 64)
i := int(100 * f)
(我这里不处理错误,因为问题不在于错误处理)
答案 0 :(得分:1)
Go使用IEEE-754个二进制浮点数。浮点数不精确。不要将它们用于金融交易。使用整数。
例如,
package main
import (
"fmt"
"strconv"
"strings"
)
func parseCents(s string) (int64, error) {
n := strings.SplitN(s, ".", 3)
if len(n) != 2 || len(n[1]) != 2 {
err := fmt.Errorf("format error: %s", s)
return 0, err
}
d, err := strconv.ParseInt(n[0], 10, 56)
if err != nil {
return 0, err
}
c, err := strconv.ParseUint(n[1], 10, 8)
if err != nil {
return 0, err
}
if d < 0 {
c = -c
}
return d*100 + int64(c), nil
}
func main() {
s := "12.34"
fmt.Println(parseCents(s))
s = "-12.34"
fmt.Println(parseCents(s))
s = "12.3"
fmt.Println(parseCents(s))
}
游乐场:https://play.golang.org/p/AwXg4_jp8lo
输出:
1234 <nil>
-1234 <nil>
0 format error: 12.3
答案 1 :(得分:-2)
如果您可以保证该金额始终以两位数为单位,当然解决方法很简单,只需删除。并解析为整数,就不需要浮点数。
不,这并不安全,请考虑$ 12、12.4 12.00以及四舍五入错误,因为您首先要通过浮点数(浮点数在设计上是不精确的)。我认为最好先将字符串转换为美分(确保它具有。确保在。之后具有两位数字,然后除去数字之外的任何内容),然后转换不必经过浮点运算。
如果这是来自用户输入或您无法控制的其他服务,则应使用意外的输入为此编写一些表测试。像这样:
https://play.golang.org/p/AiikcIMwgZP
var tests = map[string]int64{
"10": 1000,
"12.34": 1234,
"12.3": 1230,
"€12.3": 1230,
"$12.99": 1299,
"123": 12300,
"12344444444.04": 1234444444404,
"$12.991": 1299,
"0.29": 29,
}
func parseCents(s string) (int64, error) {
// Check it has . if not add 00
if !strings.Contains(s, ".") {
s += ".00"
}
// Check it has two digits after . if not add 0
if strings.Index(s, ".") > len(s)-3 {
s += "0"
}
// Remove dot to get cents
s = strings.Replace(s, ".", "", 1)
// Remove any stray formatting users might add
s = strings.Trim(s, "£€$ ")
// Parse int
return strconv.ParseInt(s, 10, 64)
}
有关使用浮点数作为货币的更多信息,请参见以下答案(这在所有语言中都是一个问题):