Getting the decimal part of a double in Swift
Asked Answered
J

10

47

I'm trying to separate the decimal and integer parts of a double in swift. I've tried a number of approaches but they all run into the same issue...

let x:Double = 1234.5678
let n1:Double = x % 1.0           // n1 = 0.567800000000034
let n2:Double = x - 1234.0        // same result
let n3:Double = modf(x, &integer) // same result

Is there a way to get 0.5678 instead of 0.567800000000034 without converting to the number to a string?

Justly answered 14/7, 2015 at 1:47 Comment(1)
See this: https://mcmap.net/q/15696/-is-floating-point-math-broken/1630618Hydrocele
A
14

Without converting it to a string, you can round up to a number of decimal places like this:

let x:Double = 1234.5678
let numberOfPlaces:Double = 4.0
let powerOfTen:Double = pow(10.0, numberOfPlaces)
let targetedDecimalPlaces:Double = round((x % 1.0) * powerOfTen) / powerOfTen

Your output would be

0.5678

Avron answered 14/7, 2015 at 2:7 Comment(1)
Thanks for the answer. This is good, but unfortunately it doesn't work for me because I don't know the appropriate number of places in code beforehand. Guess I'll have to convert through strings :-/Justly
F
107

You can use truncatingRemainder and 1 as the divider.

Returns the remainder of this value divided by the given value using truncating division.

Apple doc

Example:

let myDouble1: Double = 12.25
let myDouble2: Double = 12.5
let myDouble3: Double = 12.75

let remainder1 = myDouble1.truncatingRemainder(dividingBy: 1)
let remainder2 = myDouble2.truncatingRemainder(dividingBy: 1)
let remainder3 = myDouble3.truncatingRemainder(dividingBy: 1)

remainder1 -> 0.25
remainder2 -> 0.5
remainder3 -> 0.75
Flag answered 2/3, 2017 at 6:31 Comment(6)
simple and preciseQuad
let d: Double = 12.2 let rem = d.truncatingRemainder(dividingBy: 1) //0.1999999999999993Steinmetz
@Steinmetz 0.1999999999999993 is the same as 0.20. You just need to use NumberFormatter when displaying it to the user. If you need more precision you can use Decimal and use the string initialiser. #588504Encomium
@LeoDabus thanks, but what if I need this 0.2 later in the logic? How do you deal with that?Steinmetz
@Steinmetz sorry for the late reply. again If you need more precision you can use DecimalEncomium
I wouldn't recommend this because of the precision issue.Alpine
E
33

Same approach as Alessandro Ornano implemented as an instance property of FloatingPoint protocol:

Xcode 11 • Swift 5.1

import Foundation

extension FloatingPoint {
    var whole: Self { modf(self).0 }
    var fraction: Self { modf(self).1 }
}

1.2.whole    // 1
1.2.fraction // 0.2

If you need the fraction digits and preserve its precision digits you would need to use Swift Decimal type and initialize it with a String:

extension Decimal {
    func rounded(_ roundingMode: NSDecimalNumber.RoundingMode = .plain) -> Decimal {
        var result = Decimal()
        var number = self
        NSDecimalRound(&result, &number, 0, roundingMode)
        return result
    }
    var whole: Decimal { rounded(sign == .minus ? .up : .down) }
    var fraction: Decimal { self - whole }
}

let decimal = Decimal(string: "1234.99999999")!  // 1234.99999999
let fractional = decimal.fraction                // 0.99999999
let whole = decimal.whole                        // 1234
let sum = whole + fractional                     // 1234.99999999

let negativeDecimal = Decimal(string: "-1234.99999999")!  // -1234.99999999
let negativefractional = negativeDecimal.fraction         // -0.99999999
let negativeWhole = negativeDecimal.whole                 // -1234
let negativeSum = negativeWhole + negativefractional      // -1234.99999999
Encomium answered 5/3, 2019 at 19:48 Comment(6)
It doesn't work on 1234.567. I changed my answer accordingly.Stumpy
Any way to convert it back to a double?Alpine
@Alpine (decimal as NSDecimalNumber).doubleValueEncomium
This answer works perfectly if you need precisionWirewove
@LeoDabus I found a small error when converting the fractional part back to a Double. The initial num is 24.745093729987275. I follow the code from your answer and get let decimal = Decimal(string: num.description)!; let whole = decimal.whole; let fractional = decimal.fraction which works fine. But when I convert the fractional to a Double let f: Double = (fractional as NSDecimalNumber).doubleValue I get 0.7450937299872751. There is an extra 1 at the end. Or this num 27.305615820994717, the fractional is rounded down to 0.30561582099471696 when converting it to a Double.Wirewove
You can’t represent every value with double. Make sure to use Decimal only. Why do you need to convert to Double? You can try using a string to initialize your double but I recommend not using it at allEncomium
T
16

Swift 2:

You can use:

modf(x).1

or

x % floor(abs(x))
Taradiddle answered 25/4, 2016 at 8:39 Comment(1)
This also produces the same precision issue as with truncatingRemainder. For example print(modf(35.46).1) outputs 0.46000000000000085 which can break the equatable and comparable methods, sometimes flip the sign, and in some edge cases divide by zero depending on your logic.Alpine
A
14

Without converting it to a string, you can round up to a number of decimal places like this:

let x:Double = 1234.5678
let numberOfPlaces:Double = 4.0
let powerOfTen:Double = pow(10.0, numberOfPlaces)
let targetedDecimalPlaces:Double = round((x % 1.0) * powerOfTen) / powerOfTen

Your output would be

0.5678

Avron answered 14/7, 2015 at 2:7 Comment(1)
Thanks for the answer. This is good, but unfortunately it doesn't work for me because I don't know the appropriate number of places in code beforehand. Guess I'll have to convert through strings :-/Justly
P
9

Swift 5.1

let x:Double = 1234.5678

let decimalPart:Double = x.truncatingRemainder(dividingBy: 1)    //0.5678
let integerPart:Double = x.rounded(.towardZero)                   //1234

Both of these methods return Double value.

if you want an integer number as integer part, you can just use

Int(x)
Prosenchyma answered 10/1, 2020 at 16:14 Comment(0)
B
5

Use Float since it has less precision digits than Double

let x:Double = 1234.5678
let n1:Float = Float(x % 1)           // n1 = 0.5678
Breathy answered 14/7, 2015 at 2:1 Comment(4)
For the person who downvoted, could you please indicate any reason why you think this is bad answer?Breathy
By converting to Float you're simply throwing out more information for no benefit: that's going to give a less accurate answer (0.567799985408782958984375 instead of 0.567800000000033833202905952930450439453125).Barring
@MarkDickinson isn't that what the OP wanted to accomplish?Breathy
What, a value that's even further away from 0.5678? I doubt it.Barring
V
3

There’s a function in C’s math library, and many programming languages, Swift included, give you access to it. It’s called modf, and in Swift, it works like this

// modf returns a 2-element tuple,

// with the whole number part in the first element,

// and the fraction part in the second element

let splitPi = modf(3.141592)

splitPi.0 // 3.0

splitPi.1 // 0.141592

You can create an extension like below,

extension Double {

    func getWholeNumber() -> Double {

        return modf(self).0

    }

    func getFractionNumber() -> Double {

        return modf(self).1

    }

}
Viquelia answered 2/1, 2020 at 11:43 Comment(1)
This was already posted by @AlessandroOrnano 6 years ago. Btw I have already posted a generic extension which works for Double as wellEncomium
A
0

You can get the Integer part like this:

let d: Double = 1.23456e12

let intparttruncated = trunc(d)
let intpartroundlower = Int(d)

The trunc() function truncates the part after the decimal point and the Int() function rounds to the next lower value. This is the same for positive numbers but a difference for negative numbers. If you subtract the truncated part from d, then you will get the fractional part.

func frac (_ v: Double) -> Double
{
    return (v - trunc(v))
}

You can get Mantissa and Exponent of a Double value like this:

let d: Double = 1.23456e78

let exponent = trunc(log(d) / log(10.0))

let mantissa = d / pow(10, trunc(log(d) / log(10.0)))

Your result will be 78 for the exponent and 1.23456 for the Mantissa.

Hope this helps you.

Attic answered 28/6, 2019 at 11:18 Comment(0)
S
0

It's impossible to create a solution that will work for all Doubles. And if the other answers ever worked, which I also believe is impossible, they don't anymore.

let _5678 = 1234.5678.description.drop { $0 != "." } .description // ".5678"
Double(_5678)  // 0.5678

let _567 = 1234.567.description.drop { $0 != "." } .description // ".567"
Double(_567) // 0.5669999999999999
Stumpy answered 14/4, 2020 at 18:10 Comment(0)
A
0
extension Double {

    /// Gets the decimal value from a double.
    var decimal: Double {
        Double("0." + string.split(separator: ".").last.string) ?? 0.0
    }
    
    var string: String {
        String(self)
    }
}

This appears to solve the Double precision issues.

Usage:

print(34.46979988898988.decimal) // outputs 0.46979988898988
print(34.46.decimal) // outputs 0.46
Alpine answered 5/1, 2021 at 11:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.