Swift 4:Strange Double和Float行为

时间:2018-01-22 22:23:58

标签: macos floating-point double swift4

基本上问题是在这个例子中:

let d1 = NSNumber(value: 1.4);
let d2 = d1.doubleValue;

let f1 = NSNumber(value: Float(1.4));
let f2 = d1.floatValue;

d1结果1.4
d2结果1.3999999999999999

f1结果1.4
f2结果1.3999999999999998

有谁知道为什么会这样?

我正在尝试解析JSON文件,如:

{"name": "something", "version": 1.4}

使用以下代码:

let json = try (JSONSerialization.jsonObject(with: someData) as? [String: Any])!;
let version: Double = (json["version"] as! NSNumber).doubleValue;

OR

let version: Double = json["version"] as! Double;

OR

let version: Double = json["version"] as! Float;

而我无法获得 1.4 ......

舍入数字对我来说不是一个解决方案,因为我想将这个数字写回JSON文件,这将被解析为其他程序/语言,并且需要精确到1.4在文件中。

有什么建议吗?

更新:问题仅为 1.11.4没有问题 1.2, 1.3, 1.5

更新2:序列化代码:

    let jsonDict: Dictionary<String,Any> = [
        "name" : name,
        "version" : version
    ];

    let data = try? JSONSerialization.data(withJSONObject: jsonDict, options: []);
    let jsonString = String(data:data, encoding:.utf8);

2 个答案:

答案 0 :(得分:2)

好的,只是为了完成讨论。

最后Decimal类型做了伎俩。所以我将所有变量引用更改为Decimal NOT NSDecimalNumber,因为我收到的错误是它不符合Codable和{{1}协议。也许有一种解决方法,但最简单的解决方案就是坚持使用Decodable

我要感谢@JamesBucanek和@EricPostpischil加入讨论并帮助解决这个问题!!!

答案 1 :(得分:0)

我有同样的故事。

我需要使用 API 端点发送/接收浮点值。所以我听从了建议,将 Double 改为 Decimal。它适用于编码数据,但不适用于解码。

let value = "0.0006"
let decimal = Decimal(string: value)!
print(decimal) // 0.0006 OK
let jsonData = try JSONEncoder().encode(decimal)
print(String(data: jsonData, encoding: .utf8)!) // 0.0006 OK
let decocdedDecimal = try JSONDecoder().decode(Decimal.self, from: jsonData)
print(decocdedDecimal) // 0.0005999999999999998976 NOOOOOO!!!

但是解码到 Double 工作正常。

let decocdedDouble = try JSONDecoder().decode(Double.self, from: jsonData)
print(decocdedDouble) // 0.0006 OK

同样如上面的回答中提到的 - Decimal 应该用 String 初始化以正确编码

let decimal = Decimal(0.0006)
print(decimal) // 0.0005999999999999998976, it will be encoded same way

当然 Double 编码没有按预期工作,否则我们不会遇到这样的问题。

// 0.00059999999999999995 for all cases ofcourse
print(String(data: try JSONEncoder().encode(0.0006), encoding: .utf8)!)
print(String(data: try JSONEncoder().encode(decocdedDouble), encoding: .utf8)!)
print(String(data: try JSONEncoder().encode(Double(value)!), encoding: .utf8)!)

所以我现在的肮脏解决方案是对 Double 值使用包装器。它会将值解码为 Double,但编码为 Decimal(string:)

struct CodableDouble: Codable {
    var value: Double
    
    init(_ value: Double) {
        self.value = value
    }
    
    init(from decoder: Decoder) throws {
        value = try decoder.singleValueContainer().decode(Double.self)
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        let decimal = Decimal(string: "\(value)") ?? 0
        try container.encode(decimal)
    }
}

但我仍然不明白处理这个问题的正确方法是什么。 (除非不使用浮点值)