Swift - 测量转换(到:)英里到英尺给出了错误的结果
Swift - Measurement convert(to:) miles to feet gives wrong result
我一直在使用 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
除了最后一个 lengthFeet 之外,它们都有效。在我的操场上(Xcode 版本 9.2 (9C40b))它 returns 5279.98687664042 英尺。我还在常规应用程序构建中进行了测试,结果相同。
知道发生了什么事吗?
可以看UnitLength
here.的定义每个长度单位都有名字和系数
英里单位的系数为1609.34
,英尺单位的系数为0.3048
。当表示为 Double
(IEEE 754 双精度浮点数)时,最接近的表示分别是 1609.3399999999999
和 0.30480000000000002
。
当您进行转换 1 * 1609.34 / 0.3048
时,您会得到 5279.9868766404197
而不是预期的 5280
。这只是 fixed-precision 浮点数学不精确的结果。
如果长度的基本单位是英里,可以减轻这种情况。这当然是非常不受欢迎的,因为世界上大多数人都没有使用这个疯狂的系统,但这是可以做到的。 Foot 可以定义为 5280
的系数,可以精确地表示为 Double
。但是现在,不是 mile->foot 不精确,而是 meter->kilometer 不精确。恐怕你赢不了。
“英里”单位在 Foundation 库中定义不正确,如
所示
print(UnitLength.miles.converter.baseUnitValue(fromValue: 1.0))
// 1609.34
其中 correct value 是 1.609344
。
作为 Foundation 库中该缺陷的解决方法,您可以定义
您的“更好”英里单位:
extension UnitLength {
static var preciseMiles: UnitLength {
return UnitLength(symbol: "mile",
converter: UnitConverterLinear(coefficient: 1609.344))
}
}
并使用它给出了预期的结果:
let lengthMiles = Measurement(value: 1, unit: UnitLength.preciseMiles)
let lengthFeet = lengthMiles.converted(to: UnitLength.feet)
print(lengthFeet) // 5280.0 ft
当然,作为,
使用单位进行计算时可能会出现舍入误差,因为测量使用
二进制浮点值作为底层存储。
但造成这种“明显偏离”结果的原因是错误的定义
英里单位。
我一直在使用 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
除了最后一个 lengthFeet 之外,它们都有效。在我的操场上(Xcode 版本 9.2 (9C40b))它 returns 5279.98687664042 英尺。我还在常规应用程序构建中进行了测试,结果相同。
知道发生了什么事吗?
可以看UnitLength
here.的定义每个长度单位都有名字和系数
英里单位的系数为1609.34
,英尺单位的系数为0.3048
。当表示为 Double
(IEEE 754 双精度浮点数)时,最接近的表示分别是 1609.3399999999999
和 0.30480000000000002
。
当您进行转换 1 * 1609.34 / 0.3048
时,您会得到 5279.9868766404197
而不是预期的 5280
。这只是 fixed-precision 浮点数学不精确的结果。
如果长度的基本单位是英里,可以减轻这种情况。这当然是非常不受欢迎的,因为世界上大多数人都没有使用这个疯狂的系统,但这是可以做到的。 Foot 可以定义为 5280
的系数,可以精确地表示为 Double
。但是现在,不是 mile->foot 不精确,而是 meter->kilometer 不精确。恐怕你赢不了。
“英里”单位在 Foundation 库中定义不正确,如
所示print(UnitLength.miles.converter.baseUnitValue(fromValue: 1.0))
// 1609.34
其中 correct value 是 1.609344
。
作为 Foundation 库中该缺陷的解决方法,您可以定义
您的“更好”英里单位:
extension UnitLength {
static var preciseMiles: UnitLength {
return UnitLength(symbol: "mile",
converter: UnitConverterLinear(coefficient: 1609.344))
}
}
并使用它给出了预期的结果:
let lengthMiles = Measurement(value: 1, unit: UnitLength.preciseMiles)
let lengthFeet = lengthMiles.converted(to: UnitLength.feet)
print(lengthFeet) // 5280.0 ft
当然,作为