在ObjC中格式化非常大的十进制数的意外行为

时间:2012-12-19 13:19:22

标签: objective-c

我遇到意外的行为,使用NSNumberFormatter在ObjC中格式化非常大的数字。

似乎数字格式化器在第15位之后舍入小数(NSDecimalNumber)而不管小数位数。

以下测试在值1,3和5上失败。

两个请求:

  • 非常感谢有关替代代码的任何建议吗?
  • 我认为由于在NSNumberFormatter中使用了硬编码的数字限制,问题正在发生?

如果出现问题,帖子here会列出一个没有足够说明的解决方法。此外,我们的应用程序(银行部门)遍布多个国家/地区,我们将格式链接到后端配置的用户区域设置。这种解决方法意味着我们编写自己的数字格式化程序来处理需求。我不想做的事。

- (void)testFormatterUsingOnlySDK {
    NSDecimalNumber *value1 = [NSDecimalNumber decimalNumberWithMantissa: 9423372036854775808u exponent:-3 isNegative:YES];
    NSDecimalNumber *value2 = [NSDecimalNumber decimalNumberWithMantissa: 9999999999999990u exponent:-3 isNegative:YES];
    NSDecimalNumber *value3 = [NSDecimalNumber decimalNumberWithMantissa: 9999999999999991u exponent:-3 isNegative:YES];
    NSDecimalNumber *value4 = [NSDecimalNumber decimalNumberWithMantissa: 99999999999999900u exponent:-4 isNegative:YES];
    NSDecimalNumber *value5 = [NSDecimalNumber decimalNumberWithMantissa: 11111111111111110u exponent:-4 isNegative:YES];

    NSNumberFormatter *formatter = [[[NSNumberFormatter alloc] init] autorelease];
    formatter.allowsFloats = YES;
    formatter.maximumFractionDigits = 3;

    [self assertStringAreEqualWithActual:[formatter stringFromNumber:value1] andExpeted: @"-9423372036854775.808"];
    [self assertStringAreEqualWithActual:[formatter stringFromNumber:value2] andExpeted: @"-9999999999999.99"];
    [self assertStringAreEqualWithActual:[formatter stringFromNumber:value3] andExpeted: @"-9999999999999.991"];
    [self assertStringAreEqualWithActual:[formatter stringFromNumber:value4] andExpeted: @"-9999999999999.99"];
    [self assertStringAreEqualWithActual:[formatter stringFromNumber:value5] andExpeted: @"-1111111111111.111"];
}

- (void)assertStringAreEqualWithActual:(NSString *)actual andExpeted:(NSString *)expected {
    STAssertTrue([expected isEqualToString:actual], @"Expected %@ but got %@", expected, actual);
}

3 个答案:

答案 0 :(得分:4)

不幸的是,NSNumberFormatter无法与NSDecimalNumber一起正常使用。 问题(很可能)是它首先要做的就是调用它想要格式化的数字doubleValue

另见NSDecimalNumber round long numbers

在使用NSNumberFormatter多次尝试后,我创建了自己的格式化程序,实际上非常简单:

  1. 处理NaN
  2. 使用roundToScale:
  3. 进行回合
  4. 获取stringValue
  5. 检查是否为否定,移除前导-
  6. 查找小数点(.
  7. 本地化小数点([locale objectForKey:NSLocaleDecimalSeparator]
  8. 添加分组分隔符([locale objectForKey:NSLocaleGroupingSeparator]
  9. 如果您是格式化货币,则添加前导-或将数字放入括号中。
  10. 完成。

答案 1 :(得分:1)

您应该从这个开源代码编译自己的NSNumberFormatter,更改前缀。这应该允许您调试格式并理解为什么会发生这种情况。最糟糕的情况是,您可以向Apple提交补丁。

http://code.google.com/p/cocotron/source/browse/Foundation/NSNumberFormatter.m?r=7542c3a7ef0ef75479e6154a75d304113f5a9738

答案 2 :(得分:1)

您已将maximumFractionDigits设置为两个。所有失败的测试都在预期值中有三个小数位。期望或代码需要更改才能匹配。如果我做了这个改变:

formatter.maximumFractionDigits = 3;

然后满足您的所有测试用例。