1

I want to round a double down to 1 decimal place. For example if I have a double let val = 3.1915 I want to round this down to 3.1. Normal rounding functions will round it to 3.2 but I want to basically just drop the remaining decimal places. What is the best way to do this? Is there a native function for this? I know this is pretty straight forward to do but I want to know what the best way to do this would be where I am not using any kind of workaround or bad practices. This is not a duplicate of other rounding questions because I am not asking about about rounding, I am asking how to drop decimal places.

Similarly, if the value was 3.1215, it would also round to 3.1

Nevin Jethmalani
  • 2,726
  • 4
  • 23
  • 58

2 Answers2

3

Use the function trunc() (which stands for truncate) which will chop away the decimal portion without rounding. Specifically, multiply the Double value by 10, truncate it, then divide by 10 again. Then, to display using 1 decimal place, use String(format:):

let aDouble = 1.15
let truncated = trunc(aDouble * 10) / 10 
let string = String(format: "%.1f", truncated
print(string)

(displays "1.1")

or, to process an entire array of sample values:

let floats = stride(from: 1.099, to: 2.0, by: 0.1)

let truncs = floats
  .map { trunc($0 * 10) / 10 }
  .map { String(format: "%.1f", $0) }

let beforeAndAfter = zip(floats, truncs)
    .map { (float: $0.0, truncString: $0.1)}

beforeAndAfter.forEach { print(String(format: "%.3f truncated to 1 place is %@", $0.0, $0.1)) }

Outputs:

1.099 truncated to 1 place is 1.0
1.199 truncated to 1 place is 1.1
1.299 truncated to 1 place is 1.2
1.399 truncated to 1 place is 1.3
1.499 truncated to 1 place is 1.4
1.599 truncated to 1 place is 1.5
1.699 truncated to 1 place is 1.6
1.799 truncated to 1 place is 1.7
1.899 truncated to 1 place is 1.8
1.999 truncated to 1 place is 1.9
Duncan C
  • 128,072
  • 22
  • 173
  • 272
1

By your example I assume you meant you want to Truncate, if so using multiply and casting into Int then Dividing and casting back into Float/Double will do.

Example: 3.1915 -> 3.1

var val = 3.1915
let intVal:Int = Int(val*10)
val = Float(intVal)/10.0

print(val) //3.1

If you want more decimal places simply multiply and divide by 100 or 1000 instead.

Then if for any reason you want to use the round() function there is a overloaded variant that accepts a FloatingPointRoundingRule it will work like:

var val = 3.1915
val*=10  //Determine decimal places
val.round(FloatingPoint.towardZero) // .down is also available which differs in rounding negative numbers.
val*=0.1  //This is a divide by 10

print(val) //3.1

In practical usage I'd suggest making an extension or global function instead of writing this chunk every time. It would look something like:

extension Float {
    func trunc(_ decimal:Int) {
        var temp = self
        let multiplier = powf(10,decimal) //pow() for Double
        temp = Float(Int(temp*multiplier))/multiplier  //This is actually the first example put into one line
        return temp
    }
}

And used:

var val = 3.1915

print(val.trunc(1))  //3.1
Ben Ong
  • 913
  • 1
  • 10
  • 25
  • Using `print(val)` won't always give the desired results due to the errors inherent in binary floating point. Better to use `print(String(format:"%.1f", val))` – Duncan C Jan 26 '18 at 03:54
  • True... but print is more of a debug function meant to check the value of this variable during runtime, I'd rather have the precise value than a formatted one in case there are some errors in-between resulting in the val becoming say 3.19 which will result in subsequent calculations to be wrong but not reflected in print(). I'd agree to using String formatting for user visible display of values too to prevent the 3.100000001 problem :) – Ben Ong Jan 26 '18 at 04:03
  • I was just printing as an illustration. The simple fact is that there is no concept of decimal places in binary floating point. If you want to show a decimal number to a specific number of decimal places, you either need to convert it to a string using `String(format:)` or a `NumberFormatter` or use the `DecimalNumber` class, which represents numbers internally in base 10 so they really can store a specific number of decimal places. – Duncan C Jan 26 '18 at 14:24
  • In my answer I map the Double value(s) to strings with the desired number of decimal places. – Duncan C Jan 28 '18 at 01:53