Swift: Casting a FloatingPoint conforming value to Double
The problem there is that you need to extend BinaryFloatingPoint
instead of FloatingPoint
protocol:
extension BinaryFloatingPoint {
// Creates a new instance from the given value if possible otherwise returns nil
var double: Double? { Double(exactly: self) }
// Creates a new instance from the given value, rounded to the closest possible representation.
var doubleValue: Double { .init(self) }
}
Playground testing
let cgFloat: CGFloat = .pi // 3.141592653589793
let exactlyDouble = cgFloat.double // 3.141592653589793
let closestDoubleValue = cgFloat.doubleValue // 3.141592653589793
You can also create generic methods to return any floating point type that conforms to the BinaryFloatingPoint
protocol:
extension BinaryFloatingPoint {
func floatingPoint<F: BinaryFloatingPoint>() -> F? { F(exactly: self) }
func floatingPointValue<F: BinaryFloatingPoint>() -> F { .init(self) }
}
Playground testing
let float80pi: Float80 = .pi // 3.1415926535897932385
let closestCGFloatPiValue: CGFloat = float80pi.floatingPointValue() // 3.141592653589793
let closestDoublePiValue: Double = float80pi.floatingPointValue() // 3.141592653589793
let closestFloatPiValue: Float = float80pi.floatingPointValue() // 3.141593
let exactlyCGFloat: CGFloat? = float80pi.floatingPoint() // nil
let exactlyDouble: Double? = float80pi.floatingPoint() // nil
let exactlyFloat: Float? = float80pi.floatingPoint() // nil
Initialising String from a BinaryFloatingPoint protocol
The String init
method expects a concrete type.
Use String Interpolation instead
extension Numeric where Self: BinaryFloatingPoint {
var toString: String {
return "\(self)"
}
}
Swift random float between 0 and 1
Try initializing the divisor as a float as well, a la:
CGFloat(Float(arc4random()) / Float(UINT32_MAX))
What is the best way of converting NON-String type to String type in Swift, using initializer vs \()?
No it is not the only way of converting it. You can add another constrain to your generic type requiring it to conform to LosslessStringConvertible
as well. Note that all BinaryFloatingPoint
types conforms to CustomStringConvertible
but not all of them conforms to LosslessStringConvertible
(i.e CGFloat).
If you don't care about your method supporting CGFloat
you can constrain it LosslessStringConvertible
otherwise you need to use CustomStringConvertible
's String(describing:)
initializer.
This will not support CGFloat
func test<T: BinaryFloatingPoint & LosslessStringConvertible>(value: T) {
let stringValue = String(value)
print(stringValue)
}
This will support all BinaryFloatingPoint
floating point types. Note that you don't need to constrain to CustomStringConvertible
. It is only for demonstration purposes.
func test<T: BinaryFloatingPoint & CustomStringConvertible>(value: T) {
let stringValue = String(describing: value)
print(stringValue)
}
You can also make CGFloat
conform to LosslessStringConvertible
as well:
extension CGFloat: LosslessStringConvertible {
private static let formatter = NumberFormatter()
public init?(_ description: String) {
guard let number = CGFloat.formatter.number(from: description) as? CGFloat else { return nil }
self = number
}
}
This will allow you to support all floating point types with your generic method and use the String initializer as well.
Generic half func in Swift
You write "all numeric types", but based on the 0.5
multiplication this answer is based on the assumption that you refer to the commonly use floating point types (Double
, Float
, CGFloat
).
You could create a custom protocol that blueprints the *
function (for multiplying to values of Self
) as well as an initializer by FloatLiteralType
, and conform Double
, Float
and CGFloat
to this protocol. The *
function as well as the initializer are already implemented for these three types, so the conformance is simply explicitly telling the compiler that these types indeed conform to your custom protocol.
E.g.
protocol MyFloatingPointTypes {
func *(lhs: Self, rhs: Self) -> Self
init(_: FloatLiteralType)
}
extension Double : MyFloatingPointTypes {}
extension Float : MyFloatingPointTypes {}
extension CGFloat : MyFloatingPointTypes {}
func half<T: MyFloatingPointTypes>(value: T) -> T {
return value * T(0.5)
}
/* example usage */
var dVal: Double = 10.5
var fVal: Float = 10.5
var cfVal: CGFloat = 10.5
dVal = half(dVal)
fVal = half(fVal)
cfVal = half(cfVal)
print(dVal, fVal, cfVal) // 5.25 5.25 5.25
Alternative: compound protocol constraint, using FloatLiteralConvertible
As @Hamish writes in his comments below, rather than blueprinting the initializer by Double
(init(_: FloatLiteralType)
in MyFloatingPointTypes
, a better approach is to instead add an additional type constraint to the generic half(...)
function, constraining the generic (in addition to MyFloatingPointTypes
) to FloatLiteralConvertible
:
// lets rename this protocol here, since it no longer has any direct association with floats
protocol Multiplicable {
func *(lhs: Self, rhs: Self) -> Self
}
extension Double : Multiplicable {}
extension Float : Multiplicable {}
extension CGFloat : Multiplicable {}
func half<T: protocol<Multiplicable, FloatLiteralConvertible>>(value: T) -> T {
return value * 0.5
}
/* example usage */
// ... same as above
Another (more general) alternative: compound protocol constraint, using IntegerLiteralConvertible
Or, if you want you half
function to extend its generic "coverage" also to integer types (as also pointed out by @Hamish, thanks!) you could use the same compound protocol constraint method as above, but against IntegerLiteralConvertible
:
protocol Divisable {
func /(lhs: Self, rhs: Self) -> Self
}
extension Double : Divisable {}
extension Float : Divisable {}
extension CGFloat : Divisable {}
extension Int: Divisable {}
func half<T: protocol<Divisable, IntegerLiteralConvertible>>(value: T) -> T {
return value / 2
}
/* example usage */
var dVal: Double = 10.5
var fVal: Float = 10.5
var cfVal: CGFloat = 10.5
var iVal: Int = 11
dVal = half(dVal)
fVal = half(fVal)
cfVal = half(cfVal)
iVal = half(iVal) // truncates decimal part
print(dVal, fVal, cfVal, iVal) // 5.25 5.25 5.25 5
How to use FloatingPoint generic type for Float/Double
One problem is that FloatingPoint
is not a subprotocol of ExpressibleByFloatLiteral
, so your floating-point literals cannot necessarily be converted to T
. You can solve this either by changing FloatingPoint
to BinaryFloatingPoint
(which is a subprotocol of ExpressibleByFloatLiteral
) or by adding ExpressibleByFloatLiteral
as a separate requirement.
Then you will run into the problem that there is no pow
function that is generic over FloatingPoint
, and no member of FloatingPoint
or BinaryFloatingPoint
that performs exponentiation. You can solve this by creating a new protocol and conforming the existing floating-point types to it:
protocol Exponentiatable {
func toPower(_ power: Self) -> Self
}
extension Float: Exponentiatable {
func toPower(_ power: Float) -> Float { return pow(self, power) }
}
extension Double: Exponentiatable {
func toPower(_ power: Double) -> Double { return pow(self, power) }
}
extension CGFloat: Exponentiatable {
func toPower(_ power: CGFloat) -> CGFloat { return pow(self, power) }
}
Note that there is also a Float80
type, but the standard library doesn't provide a pow
function for it.
Now we can write a working generic function:
func srgb2linear<T: FloatingPoint>(_ S: T) -> T
where T: ExpressibleByFloatLiteral, T: Exponentiatable
{
if S <= 0.04045 {
return S / 12.92
} else {
return ((S + 0.055) / 1.055).toPower(2.4)
}
}
Related Topics
Why Use Class Only Protocols in Swift
Skspritenode Gets Hidden Below Parent Node
Make Nstextfield in Nstablecellview Firstresponder()
Class with Non-Optional Property Conforming to Protocol with Optional Property
Swift - Encoding and Decoding String for Special Characters
Nonfailable Enum Initializer with Default Value
Check If a Character Is Lowercase or Uppercase
How to Add Document with Custom Id to Firebase (Firestore) on Swift
Uibezierpath Appending Overlapping Isn't Filled
Alamofire Returns Wrong Encoding
How to Hide the Back Button from the Status Bar on the Apple Watch
Is Gamescene.Sks Not Recommended for Game Building
How to Add Detect Button Presses in Tvos
How to Cast Up to Super Class When There Is an Override Function in the Sub Class
Error "No Such Module" When Installed Framework with Pod in Swift 3
Appdelegate Segue Alternative Pass Data
Using Uilexicon to Implement Autocorrect in iOS 8 Keyboard Extension