我的服务以字符串形式接收价格,例如" 12.34"。 我必须将其保存为美分(db),因此必须为1234。以下解决方案安全吗? 安全意味着结果始终相同且正确。" 12.34"必须为1234,但不能为1235或1233。
1 2 3 | s :="12.34" f, _ := strconv.ParseFloat(s, 64) i := int(100 * f) |
(我这里不处理错误,因为问题不在于错误处理)
- 这是这篇文章的重复文章:stackoverflow.com/questions/8022389/
- 安全是什么意思?
- 安全意味着金额始终正确。 不是1233或1235,但始终是1234
- 它不安全,因为您忽略了错误。
- 当然,问题不在于如何处理错误,而在于获得适当的美分
- @Artem:不幸的是,这里的围棋社区并不热衷于帮助新来者。 您的问题是有关将浮点数转换为整数的安全性,因此我建议研究IEEE-754,然后考虑该标准与golang的关系。
-
仅为了说明使用浮点的危险性,请尝试使用
"0.29" 的代码。 您返回的结果将是28 。 在69种情况下,四舍五入误差将在零到十美元之间。 - Go的可能重复项目-货币计算
Go使用IEEE-754二进制浮点数。浮点数不精确。不要将它们用于金融交易。使用整数。
例如,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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
输出:
1 2 3 | 1234 <nil> -1234 <nil> 0 format error: 12.3 |
-
如果您执行
s :="12.3" ,则可以得到1203 play.golang.org/p/HM_A2ZmZBgg。 可能无关紧要(如果他知道字符串的末尾始终是两位数) - @dave如果您知道它总是有美分,请删除。 并解析为int
- @戴夫:谢谢。 固定。
- 如果真是这样,Id可能也会处理货币符号和空格(如果您有一个字符串字段,用户会添加这种东西),则不能仅假设数字,甚至不能假设存在。 如果这是用户输入。
- 参见下文:)我确定您会发现一些漏洞,但对于要求使用,我会犹豫。 从用户输入来看,IME将不起作用,您必须首先使用字符串,而不仅仅是返回错误。 实际取决于这是哪种系统,如果计算机在说错误也许很好(但是为什么不要求美分呢?)。
当然,如果可以保证金额始终以两位数为单位,那么解决方法很简单,只需删除。并解析为整数,就不需要浮点数。
不,这并不安全,请考虑$ 12、12.4 12.00以及四舍五入的错误,因为您首先要通过浮点数(浮点数在设计上是不精确的)。我认为最好先将字符串转换为美分(确保它具有。确保在。之后具有两位数字,然后除去数字之外的任何内容),然后转换不必经过浮点运算。
如果这是来自用户输入或您无法控制的其他服务,则应该使用意外的输入为此编写一些表测试。像这样:
https://play.golang.org/p/AiikcIMwgZP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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) } |
有关使用浮点数作为货币的更多信息,请参见以下答案(这在所有语言中都是一个问题):
Go-货币计算