我一直在使用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)),它返回5279.98687664042英尺。我还在常规应用程序构建中测试了相同的结果。
任何想法发生了什么?
答案 0 :(得分:1)
您可以看到UnitLength
here.的定义。每个长度单位都有一个名称和一个系数。
英里单位的系数为1609.34
,英尺单位的系数为0.3048
。当表示为Double
(IEEE 754双精度浮点数)时,最接近的表示分别为1609.3399999999999
和0.30480000000000002
。
当您执行转化1 * 1609.34 / 0.3048
时,您获得5279.9868766404197
而不是预期的5280
。这只是固定精度浮点数学不精确的结果。
如果长度为一英里的基本单位,则可以减轻。当然,这将是非常不可取的,因为世界上大多数人并没有使用这个疯狂的系统,但它可以做到。脚可以用5280
系数定义,可以用Double
精确表示。但现在,而不是英里 - >脚不精确,米 - >公里将是不精确的。你不能赢,我害怕。
答案 1 :(得分:1)
“基础”库中的“里程”单位定义不正确,可以通过
查看print(UnitLength.miles.converter.baseUnitValue(fromValue: 1.0))
// 1609.34
correct value为1.609344
的位置。
作为基础库中的缺陷的解决方法,您可以定义
你的“更好”里程单位:
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
当然,正如Alexander said, 由于测量使用,在使用单位进行计算时可能会发生舍入误差 二进制浮点值作为底层存储。 但是,这种“公然关闭”结果的原因是错误的定义 英里单位。