Swift未将特定值转换为浮点数。

huangapple go评论73阅读模式
英文:

Swift not converting specific values to a float

问题

你的代码似乎在尝试将 JSON 中的 1.1 转换为浮点数时出现问题,但可以正常处理其他值。这可能是由于 JSON 中的数字表示形式或浮点数的精度问题引起的。你可以尝试使用以下方法来解决问题:

  1. 确保 JSON 中的 1.1 是合法的数字表示形式,不包含额外的空格或字符。

  2. 尝试使用 Float 的初始化方法来将 JSON 中的值转换为浮点数,如下所示:

if let val = dict["atkdefval"] as? Float {
    // 此处 val 应该包含 1.1
    // 进行处理
} else {
    // 处理无法转换的情况
}

如果你尝试了这些方法仍然无法正常工作,可能需要检查 JSON 数据是否符合预期,以确保没有其他问题导致无法正确解析 1.1。

英文:

My code is not working to convert 1.1 to a float. It works to convert 1.0, 1.25, and 1.5 to floats but doesn't convert 1.1. I do not understand why this would happen as they are all decimals with very few digits of precision required. This is when decoding from a JSON.

The code in question:

if let path = Bundle.main.path(forResource: "moves", ofType: "json") {
    do {
        let data = try Data(contentsOf: URL(fileURLWithPath: path))
        let json = try JSONSerialization.jsonObject(with: data, options: [])
        if let array = json as? [[String:Any]] {
            for dict in array {
                if let actionName = dict["name"] as? String,
                   let type = dict["type"] as? String,
                   let amt = dict["amtlost"] as? Int,
                   let val = dict["atkdefval"] as? Float,
                   let dtype = dict["element"] as? String,
                   let target = dict["target"] as? Bool,
                   let steal = dict["stealAmt"] as? Float{
                    
                    let cmove = Actions(name: actionName, type: type, amtlost: amt, atkdefval: val, damageType: dtype, target: target, stealAmt: steal)
                    self.moves.append(cmove)
                } else {
                    print(dict)
                    let val = dict["atkdefval"] as? Float
                    print(val)
                }
            }
        }
    } catch {
        print("Error reading location JSON file: \(error)")
    }
} else {
    print("Could not read JSON")
}
print(self.moves.count)
for move in moves {
    print(move.name)
    print(move.atkdefval)
}

The JSON:

[
    {
        "name": "Slash",
        "type": "phys",
        "amtlost": 0,
        "atkdefval": 1.5,
        "element": "slashing",
        "target": false,
        "stealAmt": 0
    },
    {
        "name": "Punch",
        "type": "phys",
        "amtlost": 0,
        "atkdefval": 1.1,
        "element": "bludgeoning",
        "target": false,
        "stealAmt": 0
    },
    {
        "name": "Magic Missile",
        "type": "magic",
        "amtlost": 2,
        "atkdefval": 1.75,
        "element": "force",
        "target": false,
        "stealAmt": 0
    },
    {
        "name": "Block",
        "type": "defense",
        "amtlost": 0,
        "atkdefval": 1.5,
        "element": "bludgeoning",
        "target": false,
        "stealAmt": 0
    },
    {
        "name": "Healing Word",
        "type": "healing",
        "amtlost": 2,
        "atkdefval": 1.25,
        "element": "light",
        "target": false,
        "stealAmt": 0
    },
    {
        "name": "Shield",
        "type": "shield",
        "amtlost": 1,
        "atkdefval": 1.5,
        "element": "force",
        "target": false,
        "stealAmt": 0
    }
]

The class code (in case it helps):

enum ActionType:String, Codable{
    case phys = "phys"
    case magic = "magic"
    case defense = "defense"
    case healing = "healing"
    case shield = "shield"
}

enum DamageElement: String, Codable{
    case slash = "slashing"
    case pierce = "piercing"
    case bludgeon = "bludgeoning"
    case fire = "fire"
    case ice = "ice"
    case earth = "earth"
    case lightning = "lightning"
    case light = "light"
    case dark = "dark"
    case force = "force"
}

class Actions: Codable, Hashable, Equatable{
    var name: String
    var type: ActionType
    var amtlost: Int
    var atkdefval: Float
    var element: DamageElement
    var target: Bool // false is hp, true is mp
    var stealAmt: Float
    
    static func == (lhs: Actions, rhs: Actions) -> Bool {
        return lhs.name == rhs.name
    }
    
    func hash(into hasher: inout Hasher) { return hasher.combine(ObjectIdentifier(self))}
    
    init(){
        self.name = "punch"
        self.type = .phys
        self.amtlost = 0
        self.atkdefval = 4
        self.element = .bludgeon
        self.target = false
        self.stealAmt = 0
    }
    
    init(name: String, type: String, amtlost: Int, atkdefval: Float, damageType: String, target: Bool, stealAmt: Float) {
        self.name = name
        self.type = ActionType(rawValue: type.lowercased())!
        self.amtlost = amtlost
        self.atkdefval = atkdefval
        self.element = DamageElement(rawValue: damageType.lowercased())!
        self.target = target
        self.stealAmt = stealAmt
    }
    
}

I tried to have it convert from a JSON dict as a float but it converted as nil instead of the expected 1.1. I tried converting it to a string from the JSON and to a float from there and it worked but it is confusing as to why the first way doesn't work.

答案1

得分: 2

你应该按照 McKinley 描述的方式使用 JSONDecoder,但 1.1 失败的原因是默认的内部类型是 Double,1.1 的 (四舍五入的) Double 值不精确地适应于 Float。考虑:

import Foundation
NSNumber(1.1) as? Double    // 1.1
NSNumber(1.1) as? Float     // nil
NSNumber(1.5) as? Double    // 1.5
NSNumber(1.5) as? Float     // 1.5

1.5 可以在 Float 和 Double 中精确表示,因此可以转换。

JSONSerialization 将所有内容打包到 NSNumber 中,如果需要四舍五入,则无法将其 as? 桥接到 Float。你可以这样写(但我不建议):

if let actionName = dict["name"] as? String,
   let type = dict["type"] as? String,
   let amt = dict["amtlost"] as? Int,
   let val = (dict["atkdefval"] as? NSNumber)?.floatValue, // <==
   let dtype = dict["element"] as? String,
   let target = dict["target"] as? Bool,
   let steal = (dict["stealAmt"] as? NSNumber)?.floatValue // <==

你也可以通过在所有地方使用 Double 而不是 Float 来解决此问题,你应该这样做。如果有疑问,在 Swift 中使用 Double 来处理浮点数。

此外,更好的解决方案是使用 JSONDecoder,避免这些微妙的问题。由于所有类型都已经是 Decodable,你的整个解码逻辑可以被替换为:

self.moves = try JSONDecoder().decode([Actions].self, from: data)
英文:

You should use JSONDecoder as McKinley describes, but the reason this fails for 1.1 is because the default internal type is Double and the (rounded) Double value 1.1 doesn't fit in Float exactly. Consider:

import Foundation
NSNumber(1.1) as? Double    // 1.1
NSNumber(1.1) as? Float     // nil
NSNumber(1.5) as? Double    // 1.5
NSNumber(1.5) as? Float     // 1.5

1.5 can be expressed precisely in both Float and Double, so it converts.

JSONSerialization packs everything into an NSNumber, and that won't as? bridge to Float if it requires rounding. You could write it this way (but I don't recommend it):

    if let actionName = dict["name"] as? String,
let type = dict["type"] as? String,
let amt = dict["amtlost"] as? Int,
let val = (dict["atkdefval"] as? NSNumber)?.floatValue, // <==
let dtype = dict["element"] as? String,
let target = dict["target"] as? Bool,
let steal = (dict["stealAmt"] as? NSNumber)?.floatValue // <==

You can also fix this by using Double everywhere rather than Float, and you should do that anyway. When in doubt, use Double in Swift for floating point.

But in addition, the better solution is to use JSONDecoder which avoids these subtle problems. Since all the types are already Decodable, your entire decoding logic can be replaced by:

self.moves = try JSONDecoder().decode([Actions].self, from: data)

答案2

得分: 1

由于您的Actions类符合Codable,您实际上可以使用更简单的JSONDecoder而不是JSONSerialization

let listOfMoves = try JSONDecoder().decode([Actions].self, from: data)
self.moves.append(contentsOf: listOfMoves)

这样,您就不必担心手动将字典条目强制转换为FloatBool等,然后手动创建Actions。相反,JSONDecoder将为您执行所有操作,并生成一个漂亮的Actions数组——或者如果JSON格式不正确,则抛出错误。

英文:

Since your Actions class conforms to Codable, you can actually use the simpler JSONDecoder instead of JSONSerialization:

let listOfMoves = try JSONDecoder().decode([Actions].self, from: data)
self.moves.append(contentsOf: listOfMoves)

Then you don't have to worry about manually casting the entries of the dictionary to Floats and Bools, etc., and then manually creating Actions. Instead, JSONDecoder will perform all that for you and spit out a nice array of Actions -- or throw an error if the JSON is improperly formatted.

huangapple
  • 本文由 发表于 2023年5月8日 01:46:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76195414.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定