我的服务以字符串形式接收价格,例如" 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-货币计算