NSNumber -initWithDouble 和结果值的问题

Posted

技术标签:

【中文标题】NSNumber -initWithDouble 和结果值的问题【英文标题】:Issue with NSNumber -initWithDouble and resulting values 【发布时间】:2010-09-16 15:28:21 【问题描述】:

我使用 NSNumber 来存储各种值,但在使用这些值进行计算并为结果初始化新的 NSNumber 对象时有时会遇到问题。我已经想出了如何克服它,但我无法终生解释它为什么起作用,而且由于我对计算机环境(双精度、浮点数等)中数值的掌握很弱,我想问这个要学习的问题。 :-)

第一个例子是当我在不同的测量单位之间进行转换时,在这种特殊情况下,它在 mmol/L 和 mg/dl 之间。得到一个代表 mmol/L 值的 NSNumber,我提取它的 double 值并执行计算。然后我用 -initWithDouble 创建一个新的 NSNumber 并返回结果。

但是,我有一些奇怪的怪癖。如果 mmol/L 值为 10.0,则对应的 mg/dl 值为 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。

对于这个冗长的问题,如果只是我自己对处理数值的知识缺乏了解,我深表歉意,但如果有人能向我解释这一点,我将不胜感激!

谢谢,

安德斯

* 编辑 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;

【问题讨论】:

当然。我在上面的帖子中添加了单位转换示例的代码。 【参考方案1】:

尝试使用 NSDecimalNumber。这里有一个很好的教程:

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

【讨论】:

谢谢,是的,也可以试试,看看有没有什么不同。但现在的问题更多的是为什么会发生这种情况,而不是如何避免它,因为我已经想出了如何暂时规避这个问题。 :-)【参考方案2】:

如果我没记错的话,并且如果我清楚地记得大学里的适当课程,我认为这是从现实(你拥有无限的价值)到虚拟(即使你有很多但你拥有有限的价值)转换的问题: 所以你实际上不能代表所有的数字。你还必须面对你正在做的操作:乘法和除法会产生很多这样的麻烦,因为你会有很多十进制数字,而且在我看来,基于 C 的语言并不是管理这个的最佳选择种事情。 希望这将为您指明更好的信息来源:)。

【讨论】:

以上是关于NSNumber -initWithDouble 和结果值的问题的主要内容,如果未能解决你的问题,请参考以下文章

从 '(NSNumber) -> NSNumber' 转换为不相关类型 '(Any) -> Any' 总是失败

NSNumber的用法

如何增加 NSNumber

NSNumber 初始化问题

快速检查 NSNumber 是不是为 Double

如何将 NSNumber 转换和比较为 BOOL?