Проблема с NSNumber -initWithDouble и результирующими значениями - PullRequest
0 голосов
/ 16 сентября 2010

Я использую 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;
}

Ответы [ 2 ]

2 голосов
/ 16 сентября 2010

Попробуйте использовать NSDecimalNumber. Здесь есть хорошее руководство:

http://www.cimgf.com/2008/04/23/cocoa-tutorial-dont-be-lazy-with-nsdecimalnumber-like-me/

0 голосов
/ 05 ноября 2010

Если я не ошибаюсь и если я хорошо помню соответствующий курс в колледже, я думаю, что это вопрос перехода от реальности (где у вас есть бесконечные значения) к виртуальной (где у вас есть конечные значения, даже если у вас много)Таким образом, вы не можете на самом деле представить все цифры.Вам также придется столкнуться с операциями, которые вы выполняете: умножения и деления порождают много таких проблем, потому что у вас будет много десятичных чисел, и, на мой взгляд, языки на основе C не являются лучшими для управления этимвроде дело.Надеясь, это укажет вам лучший источник информации:).

...