Pau*_*aul 38 iphone cocoa objective-c
我正在寻找从double或short实例化NSDecimalNumber的最佳方法.有以下NSNumber类和实例方法......
+NSNumber numberWithFloat
+NSNumber numberWithDouble
-NSNumber initWithFloat
-NSNumber initWithDouble
Run Code Online (Sandbox Code Playgroud)
但这些似乎会返回NSNumber.另一方面,NSDecimalNumber定义以下内容:
+NSDecimalNumber decimalNumberWithMantissa:exponent:isNegative:
+NSDecimalNumber decimalNumberWithDecimal:
Run Code Online (Sandbox Code Playgroud)
这里有几种可能性.如果您将NSDecimalNumber设置为上述NSNumber便捷方法的返回值,则Xcode会生成警告.
希望能以最干净,最正确的方式投入...
orj*_*orj 70
你走在正确的轨道上(有警告,见下文).您应该能够使用NSNumber中的初始化程序,因为它们是由NSDecimalNumber继承的.
NSDecimalNumber *floatDecimal = [[[NSDecimalNumber alloc] initWithFloat:42.13f] autorelease];
NSDecimalNumber *doubleDecimal = [[[NSDecimalNumber alloc] initWithDouble:53.1234] autorelease];
NSDecimalNumber *intDecimal = [[[NSDecimalNumber alloc] initWithInt:53] autorelease];
NSLog(@"floatDecimal floatValue=%6.3f", [floatDecimal floatValue]);
NSLog(@"doubleDecimal doubleValue=%6.3f", [doubleDecimal doubleValue]);
NSLog(@"intDecimal intValue=%d", [intDecimal intValue]);
Run Code Online (Sandbox Code Playgroud)
有关此主题的更多信息,请点击此处.
但是,我在Stack Overflow和网络上看到了很多关于初始化NSDecimalNumber问题的讨论.似乎存在一些与精确度和转换为双打的问题有关的问题NSDecimalNumber
.特别是当您使用从NSNumber继承的初始化成员时.
我敲了下面的测试:
double dbl = 36.76662445068359375000;
id xx1 = [NSDecimalNumber numberWithDouble: dbl]; // Don't do this
id xx2 = [[[NSDecimalNumber alloc] initWithDouble: dbl] autorelease];
id xx3 = [NSDecimalNumber decimalNumberWithString:@"36.76662445068359375000"];
id xx4 = [NSDecimalNumber decimalNumberWithMantissa:3676662445068359375L exponent:-17 isNegative:NO];
NSLog(@"raw doubleValue: %.20f, %.17f", dbl, dbl);
NSLog(@"xx1 doubleValue: %.20f, description: %@", [xx1 doubleValue], xx1);
NSLog(@"xx2 doubleValue: %.20f, description: %@", [xx2 doubleValue], xx2);
NSLog(@"xx3 doubleValue: %.20f, description: %@", [xx3 doubleValue], xx3);
NSLog(@"xx4 doubleValue: %.20f, description: %@", [xx4 doubleValue], xx4);
Run Code Online (Sandbox Code Playgroud)
输出是:
raw doubleValue: 36.76662445068359375000 36.76662445068359375
xx1 doubleValue: 36.76662445068357953915, description: 36.76662445068359168
xx2 doubleValue: 36.76662445068357953915, description: 36.76662445068359168
xx3 doubleValue: 36.76662445068357953915, description: 36.76662445068359375
xx4 doubleValue: 36.76662445068357953915, description: 36.76662445068359375
Run Code Online (Sandbox Code Playgroud)
所以你可以看到当numberWithDouble
使用方便方法NSNumber
(你不应该因为它返回错误的指针类型而真正使用)甚至初始化initWithDouble
(IMO"应该"可以调用)时你得到一个内部的NSDecimalNumber表示(通过调用description
对象返回),表示不如调用时获得的表示decimalNumberWithString:
或decimalNumberWithMantissa:exponent:isNegative:
.
另外请注意,通过调用转换回双doubleValue
的NSDecimalNumber
情况下失去精度,但是,有趣的是,同样的不管你叫什么初始化器.
所以最后,我认为建议您在处理高精度浮点数时使用类级别decimalNumberWith*
声明的方法之一NSDecimalNumber
来创建NSDecimalNumber实例.
那么当你有双倍,浮动或其他NSNumber时,如何轻松地调用这些初始化器?
这里描述的两种方法"有效",但仍然存在精度问题.
如果您已将该号码存储为NSNumber,那么这应该有效:
id n = [NSNumber numberWithDouble:dbl];
id dn1 = [NSDecimalNumber decimalNumberWithDecimal:[n decimalValue]];
NSLog(@" n doubleValue: %.20f, description: %@", [n doubleValue], n);
NSLog(@"dn1 doubleValue: %.20f, description: %@", [dn1 doubleValue], dn1);
Run Code Online (Sandbox Code Playgroud)
但正如您从下面的输出中看到的那样,它会丢掉一些不太重要的数字:
n doubleValue: 36.76662445068359375000, description: 36.76662445068359
dn1 doubleValue: 36.76662445068357953915, description: 36.76662445068359
Run Code Online (Sandbox Code Playgroud)
如果数字是基元(浮点数或双数),那么这应该工作:
id dn2 = [NSDecimalNumber decimalNumberWithMantissa:(dbl * pow(10, 17))
exponent:-17
isNegative:(dbl < 0 ? YES : NO)];
NSLog(@"dn2 doubleValue: %.20f, description: %@", [dn2 doubleValue], dn2);
Run Code Online (Sandbox Code Playgroud)
但是你会再次遇到精度错误.正如您在下面的输出中看到的:
dn2 doubleValue: 36.76662445068357953915, description: 36.76662445068359168
Run Code Online (Sandbox Code Playgroud)
我认为这种精度损失的原因是由于浮点乘法的参与,因为以下代码工作正常:
id dn3 = [NSDecimalNumber decimalNumberWithMantissa:3676662445068359375L
exponent:-17
isNegative:NO];
NSLog(@"dn3 doubleValue: %.20f, description: %@", [dn3 doubleValue], dn3);
Run Code Online (Sandbox Code Playgroud)
输出:
dn3 doubleValue: 36.76662445068357953915, description: 36.76662445068359375
Run Code Online (Sandbox Code Playgroud)
因此,从一个double
或多个float
NSDecimalNumber进行的最一致准确的转换/初始化正在使用decimalNumberWithString:
.但是,正如Brad Larson在他的回答中指出的那样,这可能有点慢.如果性能成为问题,他使用NSScanner进行转换的技术可能会更好.
Bra*_*son 15
如果在创建NSDecimalNumber时使用NSNumber的初始值设定项,则可能会出现一些问题.请参阅此问题中发布的示例,了解其中的一个很好的例子.此外,我相信Bill Bumgarner 在cocoa-dev邮件列表中的回应中对此的看法.又见阿什利的答案在这里.
因为我对这些转换问题很偏执,所以我在过去使用了以下代码来创建NSDecimal结构:
NSDecimal decimalValueForDouble(double doubleValue)
{
NSDecimal result;
NSString *stringRepresentationOfDouble = [[NSString alloc] initWithFormat:@"%f", doubleValue];
NSScanner *theScanner = [[NSScanner alloc] initWithString:stringRepresentationOfDouble];
[stringRepresentationOfDouble release];
[theScanner scanDecimal:&result];
[theScanner release];
return result;
}
Run Code Online (Sandbox Code Playgroud)
这里复杂代码的原因是我发现NSScanner比从字符串初始化NSDecimalNumber 快约90%.我使用NSDecimal结构,因为它们可以比NSDecimalNumber兄弟产生显着的性能优势.
对于更简单但稍慢的NSDecimalNumber方法,您可以使用类似的东西
NSDecimalNumber *decimalNumberForDouble(double doubleValue)
{
NSString *stringRepresentationOfDouble = [[NSString alloc] initWithFormat:@"%f", doubleValue];
NSDecimalNumber *stringProcessor = [[NSDecimalNumber alloc] initWithString:stringRepresentationOfDouble];
[stringRepresentationOfDouble release];
return [stringProcessor autorelease];
}
Run Code Online (Sandbox Code Playgroud)
但是,如果您正在使用NSDecimalNumbers,那么您将希望尽可能避免在任何时候将值转换为浮点数.从文本字段读回字符串值并将它们直接转换为NSDecimalNumbers,使用NSDecimalNumbers进行数学运算,并将它们作为NSStrings或Core Data中的十进制值写入磁盘.一旦在任何点将值转换为浮点数,就会开始引入浮点表示错误.
归档时间: |
|
查看次数: |
26161 次 |
最近记录: |