NSNumberFormatter 没有按预期工作

NSNumberFormatter not working as expected

我们尝试对 X 小数位后的值进行 舍入 运算。为此,我们使用 NSNumberFormatter class。结果出错了

工作JAVA代码

public static double roundOff(double value) {
        DecimalFormat df2 = new DecimalFormat("#.##########");
        df2.setRoundingMode(RoundingMode.HALF_EVEN);
        return Double.parseDouble(df2.format(value));
    }
    
    public static double roundOff2(double value) {
        DecimalFormat df2 = new DecimalFormat("#.###");
        df2.setRoundingMode(RoundingMode.HALF_EVEN);
        return Double.parseDouble(df2.format(value));
    }
    public static double roundOff2Step(double value) {
        DecimalFormat df2 = new DecimalFormat("#.###");
        df2.setRoundingMode(RoundingMode.HALF_EVEN);
        return Double.parseDouble(df2.format(value));
    }

我们试过的Swift代码。

public func convertToRoundValue(number : Double)->Double{
    let formatter = NumberFormatter()
    formatter.numberStyle = .decimal
    formatter.maximumFractionDigits = 2
    formatter.roundingMode = .halfEven
    let str = String(describing: formatter.string(from: NSNumber(value:number))!)
    return str.toDouble()!
}

是否可以将 java 代码转换为 swift?此计算完全基于 银行部门 .

示例值:

8.604551872894368.61java代码

8.604551872894368.60Swift代码

我的假设:

//8.60455187289436
//8.60455187289444
//8.6045518728944
//8.604551872894
//8.60455187289
//8.6045518729
//8.604551873
//8.60455187
//8.6045519
//8.604552
//8.60455
//8.6046
//8.605
//8.61
//8.6

感谢大家

如果您想获得与 Java 相同的结果,请使用此 ceil 并四舍五入你的要求。

let value = 8.60455187289436
let stringValue = String(format: "%.2f", ceil(value*100)/100)
print(stringValue)

几点观察:

  1. 不要使用数字格式化程序进行此转换。这是非常低效的。使用 roundrounded 函数。如果你想四舍五入到小数点后两位,你可以乘以 100,四舍五入,然后除以 100。

    例如,要使用银行四舍五入法将 Double 值四舍五入到一定的小数位数,则为:

     extension Double {
         func rounded(to decimalPlaces: Int) -> Double {
             let multiplier = pow(10.0, Double(decimalPlaces))
    
             return (self * multiplier).rounded(.toNearestOrEven) / multiplier
         }
     }
    

    因此:

     let value: Double = 8.60455187289436
     let result = value.rounded(to: 2)       // 8.60
    
  2. 使用银行家四舍五入让我怀疑这是不是金融计算。如果是这样,人们应该承认许多十进制值无法在浮点类型中准确表示的问题。参见 What Every Computer Scientist Should Know About Floating-Point Arithmetic

    这个问题我最喜欢的表现是十次加0.1。对于浮点类型,这将不起作用:

     var sum: Double = 0
     let increment: Double = 0.1
    
     for _ in 0 ..< 10 {
         sum += increment
     }
    
     if sum != 1 {
         print("This is \(sum)!!!")     // This is 0.9999999999999999!!!
     }
    

    您可以使用 Decimal 来避免该问题:

     var sum: Decimal = 0
     let increment = Decimal(sign: .plus, exponent: -1, significand: 1)  // or Decimal(string: "0.1")!
    
     for _ in 0 ..< 10 {
         sum += increment
     }
    
     if sum == 1 {
         print("Total is 1")
     }
    

    最重要的是,如果处理货币值,您可以考虑使用 Decimal 而不是 Double

  3. 因此,如果您想使用 Decimal 进行舍入,您可以使用:

     extension Decimal {
         func rounded(to decimalPlaces: Int) -> Decimal {
             var input = self
             var result = Decimal()
             NSDecimalRound(&result, &input, decimalPlaces, .bankers)
             return result
         }
     }
    

    然后,再次:

     let value: Decimal = 8.60455187289436
     let result = value.rounded(to: 2)       // 8.60
    
  4. 您给出了示例,其中 8.60455187289436 使用这些例程在 Java 中四舍五入为 8.61,使用 HALF_EVEN。我不相信是这样。例如,使用:

     public double roundOff(double value) {
         DecimalFormat df = new DecimalFormat("#.##");
         df.setRoundingMode(RoundingMode.HALF_EVEN);
         return Double.parseDouble(df.format(value));
     }
    

    我得到了这些结果:

     double result1 = roundOff(8.60455187289436); // 8.60
     double result2 = roundOff(8.605);            // 8.60
     double result3 = roundOff(8.615);            // 8.62
    
     Log.d("RoundingDebug", Double.toString(result1) + " " + Double.toString(result2) + " " + Double.toString(result3));
    

    如果您得到不同的结果,请向我们展示代码。但是无论是使用银行家四舍五入还是简单四舍五入,8.60455187289436 永远不会产生 8.61.

  5. 所有讨论都假设您确实需要对结果进行四舍五入(例如,为了保存在某些数据存储中,作为未来计算的基础等)。但是,如果您的意图只是将值显示到一定数量的小数位,那么格式化程序就足够了。例如,您可以将格式化程序定义为 属性:

     let formatter: NumberFormatter = {
         let formatter = NumberFormatter()
         formatter.numberStyle = .decimal
         formatter.minimumFractionDigits = 2
         formatter.maximumFractionDigits = 2
         formatter.roundingMode = .halfEven
         return formatter
     }()
    

    但是,使用此格式化程序的唯一目的是将 UI 中的数字显示为特定小数位的本地化字符串(或用于解析用户输入)。但是人们不会使用此格式化程序将 Double 往返于 String 并返回以执行舍入。这是一种执行舍入计算的低效方法。