【问题标题】:cocoa - I've discovered what I think is a bug with NSDecimalNumber可可 - 我发现了我认为是 NSDecimalNumber 的错误
【发布时间】:2011-01-29 13:30:18
【问题描述】:

这是一个简单的代码,显示了我认为处理双数时的错误...

double wtf = 36.76662445068359375000;
id xxx = [NSDecimalNumber numberWithDouble: wtf];
NSString *myBug = [xxx stringValue];
NSLog(@"%.20f", wtf);
NSLog(@"%@", myBug);
NSLog(@"-------\n");

终端会显示两个不同的数字

36.76662445068359375000 和

36.76662445068359168

这是一个错误还是我遗漏了什么?

如果第二个数字被四舍五入,顺便说一句,这是一个非常奇怪的四舍五入...

= = = = = = = = = =

我正在编辑原始问题以包含另一个 WTF 错误...

试试这个:

修改原始数字并将其截断为 10 位小数...所以...

double wtf = 36.76662445068359375000;
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setMaximumFractionDigits:10];

NSString *valueX = [formatter stringFromNumber:[NSDecimalNumber numberWithDouble:wtf]];
[formatter release];
NSLog(@"%@", valueX);

现在的答案是 36.7666244507

现在该值是一个包含 10 个十进制数字的字符串...现在让我们将其转换回双精度

double myDoubleAgain = [valueX doubleValue];
NSLog(@"%.20f", myDoubleAgain);

答案是 36.76662445070000018177 ??????

myDoubleAgain 现在有更多数字了!!!!

【问题讨论】:

标签: iphone cocoa iphone-sdk-3.0


【解决方案1】:

通常,我会进来向人们解释他们输入的数字不能表示为浮点数,以及四舍五入的错误在哪里,等等等等。

这个问题比我们通常看到的要有趣得多,它准确地说明了“浮点不精确,请阅读'每个计算机科学家都应该知道的...... “哈哈”。

36.76662445068359375 不仅仅是任何 19 位十进制数。它恰好是一个 19 位十进制数,也可以用双精度二进制浮点数精确表示。因此,初始转换隐含在:

double wtf = 36.76662445068359375000;

是准确的。 wtf 正好包含 b100100.11000100010000011,并且没有发生舍入。

NSDecimalNumber 的规范说它将数字表示为 38 位十进制尾数和 [-127,128] 范围内的十进制指数,因此wtf 中的值也可以精确地表示为 NSDecimalNumber。因此,我们可以得出结论,numberWithDouble 没有提供正确的转换。尽管我找不到声称此转换例程已正确舍入的文档,但没有充分的理由不这样做。 这是一个真正的错误please report it

我注意到 iPhoneOS 上的字符串格式化程序似乎可以提供正确的舍入结果,因此您可以通过首先将双精度格式化为具有 38 位精度的字符串,然后使用 decimalNumberWithString 来解决这个问题。不理想,但它可能对你有用。

【讨论】:

  • 如果他没有使用 numberWithDouble,您可能会说得有道理。该消息不是为 NSDecimalNumber 定义的,而是为其超类 NSNumber 定义的。没有 NSDecimalNumber 的 API 可以方便地直接从 double 中创建实例。
  • 很好的侦探工作。如果我可以为此创建一个可重现的测试用例,我将提交一份错误报告。
  • 谢谢,我已经填写了一个错误,我还有另一个证明。我将包括在我原来的问题中。
  • @David Schaefgen:希望 NSDecimalNumber 提供自己的 numberWithDouble 实现,覆盖 NSNumber 实现。
  • @David Schaefgen:即使它只返回一个 NSNumber,而不是 NSDecimalNumber,NSNumber 也应该能够正确地表示这个特定的双精度并将其注销。这里的导入出了点问题。
【解决方案2】:

使用double 尝试超过大约 16 位的十进制精度通常是not recommended。坦率地说,我很惊讶您能够以在注销时保持该精度的方式表示此 double(具有 19 个有效数字)。您甚至可能会在 iPhone 本身上得到不同的行为,它将 long double 类型映射到普通的 double(您的 Mac 可能在幕后将其作为长双精度处理)。

您看到的四舍五入可能发生在二进制级别 (see here for more on this),因此您不会看到您期望的十进制四舍五入。

正是由于这些原因,如果您需要这种高精度的数学运算,您将希望从头到尾完全使用 NSDecimalNumbers 或 NSDecimals。为此,不要在浮点类型之间进行转换,而是直接使用 NSStrings 来填充和导出数字(或将它们作为 NSDecimalNumbers 存储在 Core Data 中)。

例如,您可以使用以下代码解决上述问题:

id xxx = [NSDecimalNumber decimalNumberWithString:@"36.76662445068359375000"];

NSDecimalNumbers(及其等效的 C 结构 NSDecimal)最多可以处理 38 个有效数字。

【讨论】:

    猜你喜欢
    • 2012-08-20
    • 1970-01-01
    • 2010-11-29
    • 2011-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-14
    相关资源
    最近更新 更多