Swift - 测量转换(到:) 英里到英尺给出错误的结果

Jef*_*ter 5 measurement swift

我一直在使用 Measurement 对象来转换大部分长度。但我有一个奇怪的问题。如果我将英里转换为英尺,我几乎会得到正确的答案。

import Foundation

let heightFeet = Measurement(value: 6, unit: UnitLength.feet) // 6.0ft
let heightInches = heightFeet.converted(to: UnitLength.inches) // 72.0 in
let heightMeters = heightFeet.converted(to: UnitLength.meters) // 1.8288 m

let lengthMiles = Measurement(value: 1, unit: UnitLength.miles) // 1.0 mi

let lengthFeet = lengthMiles.converted(to: UnitLength.feet) // 5279.98687664042 ft

// Should be 5280.0
Run Code Online (Sandbox Code Playgroud)

除了最后一个lengthFeet之外,它们都可以工作。在我的操场(Xcode 版本 9.2 (9C40b))中,它返回 5279.98687664042 英尺。我还在常规应用程序构建中进行了测试,结果相同。

任何想法发生了什么?

Mar*_*n R 5

\xe2\x80\x9cmiles\xe2\x80\x9d 单位在 Foundation 库中定义不正确,如下所示

\n\n
print(UnitLength.miles.converter.baseUnitValue(fromValue: 1.0))\n// 1609.34\n
Run Code Online (Sandbox Code Playgroud)\n\n

其中正确的值为1.609344\n作为 Foundation 库中该缺陷的解决方法,您可以定义\n您的 \xe2\x80\x9cbetter\xe2\x80\x9d 英里单位:

\n\n
extension UnitLength {\n    static var preciseMiles: UnitLength {\n        return UnitLength(symbol: "mile",\n                          converter: UnitConverterLinear(coefficient: 1609.344))\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

并使用它给出预期的结果:

\n\n
let lengthMiles = Measurement(value: 1, unit: UnitLength.preciseMiles)\nlet lengthFeet = lengthMiles.converted(to: UnitLength.feet)\nprint(lengthFeet) // 5280.0 ft\n
Run Code Online (Sandbox Code Playgroud)\n\n

当然,正如Alexander 所说,\n在使用单位进行计算时可能会出现舍入误差,因为测量使用\n二进制浮点值作为基础存储。\n但其原因 \xe2\x80\x9c 明显偏离\xe2\x80\ x9d 结果是英里单位的\n定义错误。

\n


Ale*_*ica 2

你可以看这里的定义UnitLength每个长度单位都有一个名称和一个系数。

英里单位的系数为1609.34,英尺单位的系数为0.3048。当表示为Double(IEEE 754 双精度浮点数)时,最接近的表示形式分别是1609.33999999999990.30480000000000002

当你进行转换时1 * 1609.34 / 0.3048,你得到的5279.9868766404197并不是预期的5280。这只是固定精度浮点数学不精确的结果。

如果长度的基本单位是一英里,这种情况可以得到缓解。当然,这是非常不可取的,因为世界上大多数人并不使用这个疯狂的系统,但这是可以做到的。脚可以用系数 来定义5280,该系数可以用 精确表示Double。但现在,不再是英里->英尺不精确,而是米->公里不精确。恐怕你赢不了。