Decimal to Double conversion in Swift 3
IosSwiftMigrationType ConversionSwift3Ios Problem Overview
I'm migrating a project from Swift 2.2 to Swift 3, and I'm trying to get rid of old Cocoa data types when possible.
My problem is here: migrating NSDecimalNumber
to Decimal
.
I used to bridge NSDecimalNumber
to Double
both ways in Swift 2.2:
let double = 3.14
let decimalNumber = NSDecimalNumber(value: double)
let doubleFromDecimal = decimalNumber.doubleValue
Now, switching to Swift 3:
let double = 3.14
let decimal = Decimal(double)
let doubleFromDecimal = ???
decimal.doubleValue
does not exist, nor Double(decimal)
, not even decimal as Double
...
The only hack I come up with is:
let doubleFromDecimal = (decimal as NSDecimalNumber).doubleValue
But that would be completely stupid to try to get rid of NSDecimalNumber
, and have to use it once in a while...
Well, either I missed something obvious, and I beg your pardon for wasting your time, or there's a loophole needed to be addressed, in my opinion...
Thanks in advance for your help.
Edit : Nothing more on the subject on Swift 4.
Edit : Nothing more on the subject on Swift 5.
Ios Solutions
Solution 1 - Ios
NSDecimalNumber
and Decimal
are bridged
> The Swift overlay to the Foundation framework provides the Decimal
> structure, which bridges to the NSDecimalNumber class. The Decimal
> value type offers the same functionality as the NSDecimalNumber
> reference type, and the two can be used interchangeably in Swift code
> that interacts with Objective-C APIs. This behavior is similar to how
> Swift bridges standard string, numeric, and collection types to their
> corresponding Foundation classes. Apple Docs
but as with some other bridged types certain elements are missing.
To regain the functionality you could write an extension:
extension Decimal {
var doubleValue:Double {
return NSDecimalNumber(decimal:self).doubleValue
}
}
// implementation
let d = Decimal(floatLiteral: 10.65)
d.doubleValue
Solution 2 - Ios
Another solution that works in Swift 3 is to cast the Decimal
to NSNumber
and create the Double
from that.
let someDouble = Double(someDecimal as NSNumber)
As of Swift 4.2 you need:
let someDouble = Double(truncating: someDecimal as NSNumber)
Solution 3 - Ios
Solution that works in Swift 4
let double = 3.14
let decimal = Decimal(double)
let doubleFromDecimal = NSDecimalNumber(decimal: decimal).doubleValue
print(doubleFromDecimal)
Solution 4 - Ios
Swift 5
let doubleValue = Double(truncating: decimalValue as NSNumber)
Solution 5 - Ios
Decimal
in Swift 3 is not NSDecimalNumber
. It's NSDecimal
, completely different type.
You should just keep using NSDecimalNumber
as you did before.
Solution 6 - Ios
You are supposed to use as
operator to cast a Swift type to its bridged underlying Objective-C type. So just use as
like this.
let p = Decimal(1)
let q = (p as NSDecimalNumber).doubleValue
In Swift 4, Decimal
is NSDecimalNumber
. Here's citation from Apple's official documentation in Xcode 10.
> Important
>
> The Swift overlay to the Foundation framework provides the Decimal
> structure, which bridges to the NSDecimalNumber
class. For more
> information about value types, see Working with Cocoa Frameworks in
> Using Swift with Cocoa and Objective-C (Swift 4.1).
There's no NSDecimal
anymore.
There was confusing NSDecimal
type in Swift 3, but it seems to be a bug.
No more confusion.
Note
I see the OP is not interested in Swift 4, but I added this answer because mentioning only about (outdated) Swift 3 made me confused.
Solution 7 - Ios
In Swift open source, the implementation is actually done in Decimal.swift
, but it is private. You can re-use the code from there.
extension Double {
@inlinable init(_ other: Decimal) {
if other._length == 0 {
self.init(other._isNegative == 1 ? Double.nan : 0)
return
}
var d: Double = 0.0
for idx in (0..<min(other._length, 8)).reversed() {
var m: Double
switch idx {
case 0: m = Double(other._mantissa.0)
break
case 1: m = Double(other._mantissa.1)
break
case 2: m = Double(other._mantissa.2)
break
case 3: m = Double(other._mantissa.3)
break
case 4: m = Double(other._mantissa.4)
break
case 5: m = Double(other._mantissa.5)
break
case 6: m = Double(other._mantissa.6)
break
case 7: m = Double(other._mantissa.7)
break
default: break
}
d = d * 65536 + m
}
if other._exponent < 0 {
for _ in other._exponent..<0 {
d /= 10.0
}
} else {
for _ in 0..<other._exponent {
d *= 10.0
}
}
self.init(other._isNegative != 0 ? -d : d)
}
}
Solution 8 - Ios
For swift 5, the function is
let doubleValue = Double(truncating: decimalValue as NSNumber)
the example in the below, show the number of float.
let decimalValue: Decimal = 3.14159
let doubleValue = Double(truncating: decimalValue as NSNumber)
print(String(format: "%.3f", doubleValue)) // 3.142
print(String(format: "%.4f", doubleValue)) // 3.1416
print(String(format: "%.5f", doubleValue)) // 3.14159
print(String(format: "%.6f", doubleValue)) // 3.141590
print(String(format: "%.7f", doubleValue)) // 3.1415900