Swift - Measurement convert(to:) miles to feet gives wrong result

这一生的挚爱 提交于 2019-12-24 00:56:55

问题


I've been using the Measurement object to convert from mostly lengths. But I have a strange issue. If I convert from miles to feet I get almost the right answer.

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

They all work except the last one lengthFeet. In my playground (Xcode Version 9.2 (9C40b)) it returns 5279.98687664042 ft. I also tested in a regular app build and same results.

Any ideas what is going on?


回答1:


You can see the definition of UnitLength here. Every unit of length has a name and a coefficient.

The mile unit has a coefficient of 1609.34, and the foot unit has a coefficient of 0.3048. When represented as a Double (IEEE 754 Double precision floating point number), the closest representations are 1609.3399999999999 and 0.30480000000000002, respectively.

When you do the conversion 1 * 1609.34 / 0.3048, you get 5279.9868766404197 rather than the expected 5280. That's just a consequence of the imprecision of fixed-precision floating point math.

This could be mitigated, if the base unit of length was a mile. This would be incredibly undesirable of course, because most of the world doesn't use this crazy system, but it could be done. Foot could be defined with a coefficient of 5280, which can be represented precisely by Double. But now, instead of mile->foot being imprecise, meter->kilometer will be imprecise. You can't win, I'm afraid.




回答2:


The “miles” unit is defined incorrectly in the Foundation library, as can be seen with

print(UnitLength.miles.converter.baseUnitValue(fromValue: 1.0))
// 1609.34

where as the correct value is 1.609344. As a workaround for that flaw in the Foundation library you can define your “better” mile unit:

extension UnitLength {
    static var preciseMiles: UnitLength {
        return UnitLength(symbol: "mile",
                          converter: UnitConverterLinear(coefficient: 1609.344))
    }
}

and using that gives the intended result:

let lengthMiles = Measurement(value: 1, unit: UnitLength.preciseMiles)
let lengthFeet = lengthMiles.converted(to: UnitLength.feet)
print(lengthFeet) // 5280.0 ft

Of course, as Alexander said, rounding errors can occur when doing calculations with the units, because the measurements use binary floating point values as underlying storage. But the reason for that “blatantly off” result is the wrong definition of the miles unit.



来源:https://stackoverflow.com/questions/49373300/swift-measurement-convertto-miles-to-feet-gives-wrong-result

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!