Я использую NSNumber для хранения различных значений, но иногда сталкиваюсь с проблемами при выполнении вычислений со значениями и инициализации новых объектов NSNumber для результатов. Я понял, как преодолеть это, но я не мог на всю жизнь объяснить, почему это работает, и так как мое понимание числовых значений в компьютерных средах (удваивается, плавает и т.д.) слабое, я хотел бы спросить это вопрос для изучения. : -)
Первый пример - когда я конвертирую между разными единицами измерения, в данном конкретном случае это между ммоль / л и мг / дл. Получая NSNumber, который представляет значение mmol / L, я извлекаю его двойное значение и выполняю вычисление. Затем я создаю новый NSNumber с -initWithDouble и возвращаю результат.
Однако я получаю странные причуды. Если значение ммоль / л равно 10,0, соответствующее значение мг / дл равно 180,0 (очевидно, что скорость равна 18). Но когда мне позже понадобится позволить пользователю выбрать новое значение в представлении выбора и использовать NSNumber -intValue, чтобы получить целые цифры текущего значения (используя мое собственное расширение для получения дробных цифр), int равен 179! Я проверил все промежуточные двойные значения во время вычисления, а также новое двойное значение нового NSNumber, и все в порядке (результат 180.00000). Интересно, что это происходит не для всех значений, только для некоторых (10.0 - один из реальных примеров).
Второй пример - когда я получаю двойные значения из базы данных Sqlite3 и сохраняю их в NSNumbers. Опять же, большинство ценностей работают нормально, но иногда я получаю странные вещи обратно. Например, если я сохраню 6.7 в базе данных (проверяя, когда это будет сохранено, то это действительно значение), то, что мой NSNumber покажет после получения, будет 6.699999. (На самом деле я не могу вспомнить, на момент написания статьи, есть ли это и в базе данных, но я думаю, что это так - я могу проверить позже.)
Оба этих экземпляра можно обойти, используя промежуточное значение с плавающей запятой и NSNumber initWithFloat вместо initWithDouble. Так, например, в своем преобразовании я просто делаю float resultAsFloat = resultAsDouble и использую initWithFloat для нового NSNumber.
Извиняюсь за многословный вопрос, и если мне не хватает только моих собственных знаний о работе с числовыми значениями, но я был бы очень признателен, если бы кто-то мог мне это объяснить!
Спасибо
Anders
* РЕДАКТИРОВАТЬ 1 *
Код для примера преобразования единиц:
-(NSNumber *)convertNumber:(NSNumber *)aNumber withUnit:(FCUnit *)aUnit {
// if origin unit and target unit are the same, return original number
if ([aUnit.uid isEqualToString:self.target.uid])
return aNumber;
// determine if origin unit and target unit are comparable
if (aUnit.quantity != self.target.quantity)
return nil;
// if so, convert the number...
// get bases
double originBase;
double targetBase;
if (aUnit.metre != nil) {
originBase = [aUnit.metre doubleValue];
targetBase = [self.target.metre doubleValue];
} else if (aUnit.kilogram != nil) {
originBase = [aUnit.kilogram doubleValue];
targetBase = [self.target.kilogram doubleValue];
} else if (aUnit.second != nil) {
originBase = [aUnit.second doubleValue];
targetBase = [self.target.second doubleValue];
} else if (aUnit.quantity == FCUnitQuantityGlucose) {
// special case for glucose
if ([aUnit.uid isEqualToString:FCKeyUIDGlucoseMillimolesPerLitre]) { // mmol/L -> mg/dl
originBase = 1;
targetBase = 0.0555555555555556; // since 1 / 0.0555555555555556 = 18
} else if ([aUnit.uid isEqualToString:FCKeyUIDGlucoseMilligramsPerDecilitre]) { // mg/dl -> mmol/L
originBase = 0.0555555555555556;
targetBase = 1;
}
}
// find conversion rate
double rate = originBase / targetBase;
// convert the value
double convert = [aNumber doubleValue] * rate;
// TMP FIX: this fixes an issue where the intValue of convertedNumber would be one less
// than it should be if the number was created with a double instead of a float. I have
// no clue as to why...
float convertAsFloat = convert;
// create new number object and return it
NSNumber *convertedNumber = [[NSNumber alloc] initWithFloat:convertAsFloat];
[convertedNumber autorelease];
return convertedNumber;
}