NSNumber -initWithDouble 和结果值的问题
我使用 NSNumber 来存储各种值,但有时在使用值进行计算并为结果初始化新的 NSNumber 对象时会遇到问题。我已经想出了如何克服它,但我无法解释它为什么有效,而且由于我对计算机环境中的数值(双精度、浮点数等)的掌握很弱,我想问这个问题来学习。 :-)
第一个例子是当我在不同的测量单位之间进行转换时,在这种特殊情况下,它在 mmol/L 和 mg/dl 之间。获取代表 mmol/L 值的 NSNumber,我提取其双精度值并执行计算。然后我使用 -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。
对于这个冗长的问题,我深表歉意,如果这只是我自己所缺乏的关于使用数值的知识,但如果有人能向我解释这一点,我将非常感激!
谢谢,
Anders
* EDIT 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;
}
I'm using NSNumber to store various values, but sometimes run into issues when doing calculations with the values and initializing new NSNumber objects for the results. I've figured out how to overcome it, but I couldn't for the life of me explain why it works and since my grasp on numerical values in computer environments (doubles, floats, etc) is weak I'd like to ask this question to learn. :-)
First example is when I am converting between different units of measurement, in this particular case it's between mmol/L and mg/dl. Getting an NSNumber which represents a mmol/L value, I extract its double value and perform the calculation. Then I create a new NSNumber with -initWithDouble and return the result.
However, I get odd quirks. If the mmol/L value is 10.0, the corresponding mg/dl value is 180.0 (the rate, obviously, is simply 18). But when I later need to let the user select a new value in a picker view and use the NSNumber -intValue to get the integer digits of the current value (using my own extension for getting the fractional digits), the int is 179! I've checked all the intermediate double values during calculation as well as the new NSNumber's double value and all is fine (180.00000 is the result). Interestingly, this doesn't happen for all values, just some (10.0 being one real example).
The second example is when I retrieve double values from an Sqlite3 database and store them in NSNumbers. Again, most values work fine, but occasionally I get weird stuff back. For instance, if I save 6.7 in the database (checking when it is saved that that is in fact the value), what my NSNumber will show after retrieval is 6.699999. (I can't actually remember at the moment of writing if that's what's in the database as well, but I think it is - I can check later.)
Both of these instances can be circumvented by using an intermediate float value and NSNumber initWithFloat instead of initWithDouble. So in my conversion, for example, I just do a float resultAsFloat = resultAsDouble and use initWithFloat for the new NSNumber.
Apologies for the long-winded question and if it's just my own knowledge about working with numerical values that is lacking, but I would really appreciate if someone could explain this to me!
Thanks,
Anders
* EDIT 1 *
Code for the unit conversion example:
-(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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果我没记错的话,如果我清楚地记得大学里的相应课程,我认为这是一个从现实(你有无限的价值)到虚拟(即使你有很多价值,你也有有限的价值)的转换问题:所以你实际上并不能代表所有的数字。您还必须面对正在进行的运算:乘法和除法会产生很多此类麻烦,因为您将有很多十进制数,并且在我看来,基于 C 的语言并不是管理此问题的最佳方法一种事情。
希望这将为您提供更好的信息来源:)。
If I'm not wrong and if I well remember the appropriate course at college i think it's a matter of conversion from reality (where you have infinite values) to virtual (where you have finite values even if you have a lot): so you can't actually represent ALL the numbers. You also have to face with operations you're making: multiplications and divisions generate a lot of this troubles, because you'll have a lot of decimal numbers and, in my opinion, C-Based languages are not the best around to manage this kind of matter.
Hoping this will point you to a better source of information :).
尝试使用 NSDecimalNumber。这里有一个很好的教程:
http://www.cimgf.com/2008/04/23/cocoa-tutorial-dont-be-lazy-with-nsdecimalnumber-like-me/
Try using NSDecimalNumber. There's a good tutorial here:
http://www.cimgf.com/2008/04/23/cocoa-tutorial-dont-be-lazy-with-nsdecimalnumber-like-me/