Question

In my iPhone application, the user can change the unit (km or mile) in the preferences.

[[NSUserDefaults standardUserDefaults] setObject:@"km" forKey:@"distanceUnit"];

or

[[NSUserDefaults standardUserDefaults] setObject:@"mile" forKey:@"distanceUnit"];

I save the distance only in km in my Core-Data. I work with int. So every time I get the distance i do the following:

if ([unit isEqualToString:@"km"]) {
} else {
    intTotalKM = intTotalKM * 0.621371192;
} 

And when saving the distance, I do the following:

if ([unit isEqualToString:@"km"]) {
} else {
    intTotalKM = intTotalKM / 0.621371192;
}

For example, without changing my distance, when I save and get it, I loose 1 or 2 km/miles.

I loose precision because of 0.621371192 and because I'm using only int. I don't want to use double values. Do you have any ideas of how to deal with this problem?

Edit: I wonder if there is any other way of conversion more appropriate for my situation.

Thanks.

Was it helpful?

Solution 2

If the loss of precision that you have by converting the number one time is acceptable, then I would just quit converting back and forth every time that you display/save it, and only convert, if needed, when the value is entered by the user.

So in other words, just like in your user defaults, only ever have the value in memory represent km.

- (void)textFieldDidEndEditing:(UITextField *)textField {
    intTotalKM = [textField.text intValue];

    if ([unit isEqualToString:@"mile"]) {
        intTotalKM = intTotalKM / 0.621371192;
    }
}

Note that this conversion is only done once when the user enters a new value.

To display the value to the user, you would convert the value to miles if needed. When you save the value, you simply save intTotalKM without doing any conversion since it is always stored in KM.

Otherwise, you should store the value in such a way that you won't have this loss of precision. By storing the decimal portion of the number (using a double), by saving as a different unit (like meters - my personal favorite) or by storing the unit and using native values, all as suggested in other answers here.

OTHER TIPS

The only loss-less solution is to store the distance in the native units. Add a single-character column designating the units in which the distance is expressed, and store a code (say, 'K' for km and 'M' for miles) along with the actual distance.

If adding a column is not an option, you can put the units in the upper bits of the number that you store: since the largest distance in your app is 2^20, you can use the remaining 12 bits of the number to encode the units in which the distance is expressed. Use dist & 0xFFFFF to extract the actual distance, and dist >> 20 to extract the units code (1 for km, 0 for mi).

If you really wish to stay with integers and maintain precision use smaller units. With your maximum requirement of 999999km then if your integers are 32-bit you can store the value in meters and adjust your conversion to produce yards. If your integers are 64-bit then you can can use cm or even mm, but you probably don't need to go that far. This is how some financial calculations are done, e.g. a $1.59 is not stored as 1.59 but as 159.

You've answered the question yourself: you lose precision because you're using integers... You don't have much of a choice. Either you change to double, or you stick with int and inaccurate values...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top