我正在使用alamofire调用我的web服务:

ApiManager.manager.request(.GET, webServiceCallUrl,  parameters: ["id": 123])
        .validate()
        .responseJSON { response in
            switch response.result {
            case .Success:
                print(response.result.value!)

                //...

            case .Failure:
                //...
            }
    }

我的Web服务返回以下JSON:
{
    //...
    "InvoiceLines": [{
        "Amount": 0.94
    }]
}

Alamofire将其视为双精度而不是十进制,因此在输出控制台中,我得到:
{
    //...
    InvoiceLines =     (
                {
            Amount = "0.9399999999999999";
        }
    );
}

这将在我的代码中进一步导致舍入错误。
我使用服务器上的fiddler检查了web服务json响应,以确认它返回的是0.94。因此,我可以排除服务器的问题,并怀疑responseJSON导致了我的问题。
如何使货币值返回为正确的NSDecimalNumber值?
吉姆回答/评论后的额外信息:
var stringToTest = "{\"Amount\":0.94}"
var data = stringToTest.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
var object = try NSJSONSerialization.JSONObjectWithData(data!, options: opt)

var a = object["Amount"]
print(a) //"Optional(0.9399999999999999)"

var b = NSDecimalNumber(decimal: (object["Amount"] as! NSNumber).decimalValue)
print(b) //"0.9399999999999999"

如果我在json中将该值作为字符串传递,则可以得到所需的结果:
var stringToTest = "{\"Amount\":\"0.94\"}"
var data = stringToTest.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
var object = try NSJSONSerialization.JSONObjectWithData(data!, options: opt)

var c = NSDecimalNumber(string: (object["Amount"] as! String))
print(c) //0.94

但是我不想实现对api的更改,所以需要一个保持json格式不变的解决方案。我唯一的选择就是每次都把双倍打成圆形吗?这似乎是一种错误的做法,可能会在未来引发取整问题。

最佳答案

十进制值本身是浮点值,json规范特别允许高指数值,您可以将其视为“实”浮点值:
http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf第8节“数字”
您所描述的“十进制”是十进制浮点,在这种情况下,您有一个整数和一个基数为10的指数,即94*10^-2。很少有系统支持这种存储格式,而是使用二进制浮点,即整数和base2指数。因为2不是5,十分之一,百分之一等的因数,所以不能用二进制浮点精确表示。这意味着,内部表示总是有点偏离,但如果您尝试进行比较或打印,它会意识到精度,并将按您的预期工作。

var bar = 0.94 // 0.939999999999999
bar == 0.94 // true
print(bar) // "0.94\n"

在其他代码中遇到问题的地方,将是由于复杂的精度问题。在十进制浮点数中,如果尝试执行1/3+1/3-2/3,则得到0.333333+0.333333-0.666667=-0.000001!=0,在二进制浮点中也会遇到同样的问题。这是因为在一个或多个操作之后,该值将超出从原始数字转换的精度范围,因此按字面意思进行。
如果在这种情况下,数据实际上是固定点(例如大多数情况下的货币值),最安全的方法是将其相乘,然后只使用整数值,直到必须显示,如:
var bar = 0.94 * 100
var foo = bar * 5 // multiply price by 5
print(bar / 100)

另外,由于双精度浮点具有非常高的基精度,远远超过所需的1/100,因此在执行比较或输出时,您可以仅舍入。
var bar = 0.94
var foo = bar * 5 // multiply price by 5
print(round(bar*100) / 100)

10-06 04:31