Asked  7 Months ago    Answers:  5   Viewed   48 times

Can anyone tell me how to round a double value to x number of decimal places in Swift?

I have:

var totalWorkTimeInHours = (totalWorkTime/60/60)

With totalWorkTime being an NSTimeInterval (double) in second.

totalWorkTimeInHours will give me the hours, but it gives me the amount of time in such a long precise number e.g. 1.543240952039......

How do I round this down to, say, 1.543 when I print totalWorkTimeInHours?

 Answers

14

You can use Swift's round function to accomplish this.

To round a Double with 3 digits precision, first multiply it by 1000, round it and divide the rounded result by 1000:

let x = 1.23556789
let y = Double(round(1000*x)/1000)
print(y)  // 1.236

Other than any kind of printf(...) or String(format: ...) solutions, the result of this operation is still of type Double.

EDIT:
Regarding the comments that it sometimes does not work, please read this:

What Every Programmer Should Know About Floating-Point Arithmetic

Tuesday, June 1, 2021
 
xenon
answered 7 Months ago
35

Use "Nx" for x decimal digits.

 public void DisplayNDecimal(double dbValue, int nDecimal)
 {
   Console.WriteLine(dbValue.ToString("N" + nDecimal));
 }
Saturday, June 19, 2021
 
tika
answered 6 Months ago
59

Taking on board what you said I have modified my code slightly to cover different locales. The key was taking a value string in a localised format to a Double that is rounded based on the format string.

The format string is always a UK based format with the decimal seperators specified as "." and thousand seperators specified as ",".

I am using the DecimalFormat to initially parse the localised format based on a specified locale. This gives a Double equivalent of the string correctly. I then use a BigDecimal to handle the rounding. I can get the number of decimal places from the DecimalFormat instance and call setScale on the BigDecimal to perform the rounding.

The initial code structure has been modified to allow you to see what happens under different locale circumstances thanks @RD01 for noting importance of other locales.

I now have code as follows:

private void runTests3() {
    // output current locale we are running under
    System.out.println( "Current Locale is " + Locale.getDefault().toString() );

    // number in Central European Format with a format string specified in UK format
    String numbersInEuropeanFormatString[] = new String[] { "1.000,234567", "1,2345678", "1.222.333,234567" };
    String formatUK = "###,##0.0000";

    // output numbers using the german locale
    System.out.println("Output numbers using the German localen");
    for(String num : numbersInEuropeanFormatString ) {
        formatNumberAsDouble(num, formatUK, Locale.GERMAN);
    }

    // output numbers using the UK locale.  
    // this should return unexpected results as the number is in European format
    System.out.println("Output numbers using the UK localen");
    for(String num : numbersInEuropeanFormatString ) {
        formatNumberAsDouble(num, formatUK, Locale.UK);
    }

    // output numbers using new DecimalFormat( formatUK ) - no locale specified
    System.out.println("nnOutput numbers using new DecimalFormat( " + formatUK + " )n");
    for(String num : numbersInEuropeanFormatString ) {
        formatNumberAsDouble( num, formatUK, null);
    }
}

private void formatNumberAsDouble(String value, String format, Locale locale) {


    NumberFormat formatter;
    int decimalPlaces;

    // create the formatter based on the specified locale
    if( locale != null ) {
         formatter = NumberFormat.getNumberInstance(locale);
         // creating the above number format does not take in the format string
         // so create a new one that we won't use at all just to get the
         // decimal places in it
         decimalPlaces = (new DecimalFormat(format)).getMaximumFractionDigits();
    } else {
        formatter = new DecimalFormat( format );
        decimalPlaces = formatter.getMaximumFractionDigits();
    }

    // get the result as number
    Double result = null;
    try {
        result = formatter.parse( value ).doubleValue();
    } catch( ParseException ex ) {
        // not bothered at minute
    }

    // round the Double to the precision specified in the format string


    BigDecimal bd = new BigDecimal(result );
    Double roundedValue = bd.setScale( decimalPlaces, RoundingMode.HALF_UP ).doubleValue();

    // output summary
    System.out.println("tValue = " + value);
    System.out.println( locale == null  ? "tLocale not specified" : "tLocale = " + locale.toString());
    System.out.println( format == null || format.length() == 0 ? "tFormat = Not specified" : "tFormat = " + format);
    System.out.println("tResult (Double) = " + result);
    System.out.println("tRounded Result (Double) (" + decimalPlaces + "dp) = " + roundedValue);
    System.out.println("");
}

This produces the following output:

Current Locale is nl_BE
Output numbers using the German locale

    Value = 1.000,234567
    Locale = de
    Format = ###,##0.0000
    Result (Double) = 1000.234567
    Rounded Result (Double) (4dp) = 1000.2346

    Value = 1,2345678
    Locale = de
    Format = ###,##0.0000
    Result (Double) = 1.2345678
    Rounded Result (Double) (4dp) = 1.2346

    Value = 1.222.333,234567
    Locale = de
    Format = ###,##0.0000
    Result (Double) = 1222333.234567
    Rounded Result (Double) (4dp) = 1222333.2346

Output numbers using the UK locale

    Value = 1.000,234567
    Locale = en_GB
    Format = ###,##0.0000
    Result (Double) = 1.0
    Rounded Result (Double) (4dp) = 1.0

    Value = 1,2345678
    Locale = en_GB
    Format = ###,##0.0000
    Result (Double) = 1.2345678E7
    Rounded Result (Double) (4dp) = 1.2345678E7

    Value = 1.222.333,234567
    Locale = en_GB
    Format = ###,##0.0000
    Result (Double) = 1.222
    Rounded Result (Double) (4dp) = 1.222



Output numbers using new DecimalFormat( ###,##0.0000 )

    Value = 1.000,234567
    Locale not specified
    Format = ###,##0.0000
    Result (Double) = 1000.234567
    Rounded Result (Double) (4dp) = 1000.2346

    Value = 1,2345678
    Locale not specified
    Format = ###,##0.0000
    Result (Double) = 1.2345678
    Rounded Result (Double) (4dp) = 1.2346

    Value = 1.222.333,234567
    Locale not specified
    Format = ###,##0.0000
    Result (Double) = 1222333.234567
    Rounded Result (Double) (4dp) = 1222333.2346
Thursday, July 29, 2021
 
mikelovelyuk
answered 5 Months ago
83

Remove the NavigationView from SecondView.

The NavigationLink puts the second view inside the first views navigations view, so you do not need to put it inside a second one.

You can still update the title of the view from SecondView like so:

struct SecondView: View {
   var body: some View {
       Text("My View")
       .navigationBarTitle("Second View")
   }
}
Sunday, September 26, 2021
 
Backlin
answered 2 Months ago
91

update: Xcode 8.3.2 • Swift 3.1

extension Collection where Iterator.Element == String {
    var doubleArray: [Double] {
        return flatMap{ Double($0) }
    }
    var floatArray: [Float] {
        return flatMap{ Float($0) }
    }
}

usage:

let strNumbersArray = ["1.5","2.3","3.7","4.5"]   // ["1.5", "2.3", "3.7", "4.5"]
let doublesArray = strNumbersArray.doubleArray    // [1.5, 2.3, 3.7, 4.5]
let floatsArray = strNumbersArray.floatArray      // [1.5, 2.3, 3.7, 4.5]
let total = doublesArray.reduce(0, +)     // 12
let average = total / Double(doublesArray.count)  // 3

If you have an Array of Any? where you need to convert all strings from Optional Any to Double:

extension Collection where Iterator.Element == Any? {
    var doubleArrayFromStrings: [Double] {
        return flatMap{ Double($0 as? String ?? "") }
    }
    var floatArrayFromStrings: [Float] {
        return flatMap{ Float($0 as? String ?? "") }
    }
}

usage:

let strNumbersArray:[Any?] = ["1.5","2.3","3.7","4.5", nil]   // [{Some "1.5"}, {Some "2.3"}, {Some "3.7"}, {Some "4.5"}, nil]
let doublesArray = strNumbersArray.doubleArrayFromStrings    // [1.5, 2.3, 3.7, 4.5]
let floatsArray = strNumbersArray.floatArrayFromStrings      // [1.5, 2.3, 3.7, 4.5]
let total = doublesArray.reduce(0, +)     // 12
let average = total / Double(doublesArray.count)  // 3
Sunday, October 17, 2021
 
mgierw
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share