代码之家  ›  专栏  ›  技术社区  ›  shek

从特定于区域设置的字符串中获取NSDecimalNumber?

  •  15
  • shek  · 技术社区  · 16 年前

    examples I've seen thus far on the interwebs

    NSString *s = @"0.07";
    
    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
    [formatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
    [formatter setGeneratesDecimalNumbers:YES];
    NSDecimalNumber *decimalNumber = [formatter numberFromString:s];
    
    NSLog([decimalNumber stringValue]); // prints 0.07000000000000001
    

    我使用的是10.4模式(除了文档中推荐的模式外,它也是iPhone上唯一可用的模式),但向格式化程序指示我要生成十进制数。请注意,我简化了我的示例(实际上我处理的是货币字符串)。然而,我显然做了一些错误的事情,因为它返回了一个说明浮点数不精确的值。

    编辑:注意我的示例是为了简单。我要问的问题还应该与何时需要获取特定于语言环境的货币字符串并将其转换为NSDecimalNumber有关。此外,还可以将其扩展为特定于语言环境的百分比字符串,并将其转换为NSDecimalNumber。

    4 回复  |  直到 15 年前
        1
  •  18
  •   Ben Mosher    13 年前

    几年后:

    +(NSDecimalNumber *)decimalNumberWithString:(NSString *)numericString NSDecimalNumber

        2
  •  13
  •   Sergey Kalinichenko    11 年前

    根据Boaz Stuler的回答,我为这个问题向苹果公司记录了一个bug。在这个问题解决之前,以下是我认为最好的解决办法。这些解决方法仅仅依赖于将十进制数四舍五入到适当的精度,这是一种可以补充现有代码的简单方法(而不是从格式化程序切换到扫描程序)。

    一般数字

    基本上,我只是根据对我的情况有意义的规则对数字进行四舍五入。因此,YMMV取决于您支持的精度。

    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
    [formatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
    [formatter setGeneratesDecimalNumbers:TRUE];
    
    NSString *s = @"0.07";
    
    // Create your desired rounding behavior that is appropriate for your situation
    NSDecimalNumberHandler *roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:2 raiseOnExactness:FALSE raiseOnOverflow:TRUE raiseOnUnderflow:TRUE raiseOnDivideByZero:TRUE]; 
    
    NSDecimalNumber *decimalNumber = [formatter numberFromString:s];
    NSDecimalNumber *roundedDecimalNumber = [decimalNumber decimalNumberByRoundingAccordingToBehavior:roundingBehavior];
    
    NSLog([decimalNumber stringValue]); // prints 0.07000000000000001
    NSLog([roundedDecimalNumber stringValue]); // prints 0.07
    

    NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
    [currencyFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
    [currencyFormatter setGeneratesDecimalNumbers:TRUE];
    [currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
    
    // Here is the key: use the maximum fractional digits of the currency as the scale
    int currencyScale = [currencyFormatter maximumFractionDigits];
    
    NSDecimalNumberHandler *roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:currencyScale raiseOnExactness:FALSE raiseOnOverflow:TRUE raiseOnUnderflow:TRUE raiseOnDivideByZero:TRUE]; 
    
    // image s is some locale specific currency string (eg, $0.07 or €0.07)
    NSDecimalNumber *decimalNumber = (NSDecimalNumber*)[currencyFormatter numberFromString:s];
    NSDecimalNumber *roundedDecimalNumber = [decimalNumber decimalNumberByRoundingAccordingToBehavior:roundingBehavior];
    
    NSLog([decimalNumber stringValue]); // prints 0.07000000000000001
    NSLog([roundedDecimalNumber stringValue]); // prints 0.07
    
        3
  •  4
  •   Boaz Stuller    16 年前

    这似乎有效:

    NSString *s = @"0.07";
    
    NSScanner* scanner = [NSScanner localizedScannerWithString:s];
    NSDecimal decimal;
    [scanner scanDecimal:&decimal];
    NSDecimalNumber *decimalNumber = [NSDecimalNumber decimalNumberWithDecimal:decimal];
    
    NSLog([decimalNumber stringValue]); // prints 0.07
    

    编辑:在苹果解决这个问题之前(然后每个潜在用户都会更新到固定的OSX版本),您可能需要使用NSScanner滚动您自己的解析器,或者对输入的数字接受“仅”双精度。除非你计划在这个应用程序中加入五角大楼的预算,否则我建议你使用后者。事实上,双倍数字精确到小数点后14位,因此,如果少于一万亿美元,它们就会少掉一分钱。我不得不为一个项目编写自己的基于NSDateFormatter的日期解析例程,我花了一个月的时间处理所有有趣的edge案例(比如只有瑞典的长日期中包含了星期几)。

        4
  •  0
  •   Community CDub    7 年前

    另见 Best way to store currency values in C++

    处理货币的最佳方法是对货币的最小单位使用整数值,即美元/欧元等于美分等。这样可以避免代码中与浮点相关的精度错误。