【问题标题】:NSNumberFormatter not working as expectedNSNumberFormatter 没有按预期工作
【发布时间】:2020-07-25 07:52:31
【问题描述】:

我们尝试对 X 位小数后的值进行 四舍五入 的操作。为此,我们使用NSNumberFormatter 类。结果出错了。

工作 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代码永远不会将值四舍五入到8.61,在我看来这是正确的,所以java和swift代码都可以正常工作
  • 确实如此,因此我们面临计算问题..
  • 好吧,我在 Visual Studio Code 中测试了代码,所以如果你得到另一个结果,那么它是由这里没有解释的东西引起的。使用 roundOff2roundOff2Step 时,8.60455187289436 舍入到小数点后 2 位将得到 8.6
  • 另外,如果您正在为银行部门构建应用程序并且值代表金钱,那么我建议使用 Decimal 而不是 Double,例如参见 this question
  • 这三种方法在 Swift 中的相似代码是什么?

标签: ios swift nsnumberformatter swift5.1


【解决方案1】:

一些观察:

  1. 请勿使用数字格式化程序进行此转换。这是非常低效的。使用roundrounded 函数。如果你想四舍五入到小数点后 2 位,你可以乘以 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 中使用这些例程,使用HALF_EVEN 舍入为8.61。我不相信是这样的。例如:

     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 并返回以执行舍入。这是一种执行舍入计算的低效方式。

【讨论】:

  • 感谢您的详细解释和十进制方法。
【解决方案2】:

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

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

【讨论】:

  • “与您在 Java 中获得的结果相同”但 java 代码返回 8.6 而不是 8.61。
  • 在他的问题中,他提到了8.60455187289436 to 8.61 In java code我根据问题给出了答案。但是,我会在 Java 中检查结果是什么。
猜你喜欢
  • 2021-10-19
  • 2020-03-18
  • 2012-06-14
  • 2014-11-15
  • 1970-01-01
  • 2012-07-02
  • 2011-09-07
  • 2013-03-03
  • 2015-05-18
相关资源
最近更新 更多