-1

We trying to do some operation with Rounding the values after X number of decimals. To do that We are using NSNumberFormatter class. The results comes wrong.

Working JAVA Code

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));
    }

The Swift Code We tried.

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()!
}

Is it possible to convert java code to swift? This calculations are purely based on Banking Sectors.

Example values :

8.60455187289436 to 8.61 In java code

8.60455187289436 to 8.60 In Swift code

My assumption :

//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

Thanks to All

Rob
  • 415,655
  • 72
  • 787
  • 1,044
karthikeyan
  • 3,821
  • 3
  • 22
  • 45
  • 1
    The java code you have posted will never round the value to 8.61, which in my view is correct so both the java and the swift code works correctly – Joakim Danielson Jul 25 '20 at 08:06
  • It does, we are facing issue in calculation because of this.. – karthikeyan Jul 25 '20 at 08:10
  • 2
    Well I tested the code in Visual Studio Code so if you get another result then it is caused by something not explained here. 8.60455187289436 rounded to 2 decimals will yield 8.6 when using `roundOff2` or `roundOff2Step` – Joakim Danielson Jul 25 '20 at 08:13
  • 2
    Also if you are building an app for the bank sector and the values represents money then I would suggest using Decimal instead of Double, see [this question](https://stackoverflow.com/questions/1165761/decimal-vs-double-which-one-should-i-use-and-when) for instance. – Joakim Danielson Jul 25 '20 at 08:26
  • what would be similiar code in Swift to those three methods? – karthikeyan Jul 26 '20 at 07:22

2 Answers2

2

A few observations:

  1. Don't use number formatters for this conversion. That's very inefficient. Use the round or rounded functions. And if you want to round to 2 decimal places, you would multiple by 100, round, and then divide by 100.

    E.g., to round a Double value to a certain number of decimal places using a banker’s rounding, it would be:

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

    Thus:

    let value: Double = 8.60455187289436
    let result = value.rounded(to: 2)       // 8.60
    
  2. The use of banker’s rounding makes me wonder if this is a financial calculation. If so, one should acknowledge problems where many decimal values cannot be represented accurately in floating point types. See What Every Computer Scientist Should Know About Floating-Point Arithmetic.

    My favorite manifestation of this problem is adding 0.1 ten times. With floating point types, this will not work:

    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!!!
    }
    

    You can use Decimal to avoid that problem:

    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")
    }
    

    Bottom line, if dealing with currency values, you might consider using Decimal rather than Double.

  3. So, if you want to round using Decimal you might use:

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

    And, again:

    let value: Decimal = 8.60455187289436
    let result = value.rounded(to: 2)            // 8.60
    
  4. You gave the example where 8.60455187289436 was rounded to 8.61 in Java using these routines, using HALF_EVEN. I don't believe that’s the case. For example, with:

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

    I got these results:

    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));
    

    If you're getting different results, please show us code. But whether using banker’s rounding or just plain rounding, 8.60455187289436 will never produce 8.61.

  5. All of the discussion presumes that you really need to round the results (e.g. for saving in some data store, the basis for future calculations, etc.). If, however, your intent was simply to show the value to a certain number of decimal places, then a formatter is sufficient. For example, you might define your formatter as a property:

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

    But one would use this formatter solely for the sake of displaying the number as a localized string in the UI to a certain number of decimal places (or for parsing user input). But one would not use this formatter for round-tripping a Double to a String and back in order to perform rounding. That is an inefficient way of performing rounding calculations.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
1

If you want to get the same results as you are getting in Java use this ceil and round up to your requirement.

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

enter image description here

Rashid Latif
  • 2,809
  • 22
  • 26
  • "the same results as you are getting in Java" but the java code returns 8.6 and not 8.61. – Joakim Danielson Jul 25 '20 at 11:18
  • In his question, he mentioned ```8.60455187289436 to 8.61 In java code``` I give the answer according to the question. However, I'll check in Java what is the result. – Rashid Latif Jul 25 '20 at 12:16