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)
}
}
Swift: Casting a generic, FloatingPoint value to Int
Simple C++ templates are nothing more than a macro-ish, type less, source code replacement mechanism. The calling code is replaced with the applied template and the compiler checks if the resulting code makes any sense. You are right, a roundedInt<T>
unbounded function should work fine in C++ land.
Swift generics instead are typesafe by design, meaning the generic code must be sound on its own, independently of any particulars of a given call site. In your example, the FloatingPoint
protocol is the type guiding the compilation (and not the actual T
type used by the calling code).
(By the way, Java/C# generics also resembles Swift style as well.)
Regarding your actual problem, you could simply provide two overloads for the roundedInt
function:
func roundedInt(_ f: Float) -> Int { ... }
func roundedInt(_ f: Double) -> Int { ... }
which should cover most use cases. Of course, assuming you are only writing small helper functions with this... and not full blown libraries/frameworks!
Otherwise, try @Sweeper string-based solution. But please keep in mind that besides the potential loss of precision he correctly noted, there are also some nasty performance problems lurking in there as well.
How can I convert Generic type SignedNumeric to Float?
Not every SignedNumeric
can be converted to a Float
. Recall that to be a SignedNumeric
, you need to be able to:
- be negative and positive
- be multiplied
- be added and subtracted
- have a magnitude that is
Comparable
andNumeric
- can be converted from an integer
The last 4 requirements are all inherited from Numeric
. We can easily build our own type that can do all those things. Here's a contrived example:
struct MyNumber: SignedNumeric, Comparable {
private var secret: [String]
private var isNegative: Bool
private var number: Int { secret.count }
static func *= (lhs: inout MyNumber, rhs: MyNumber) {
lhs = lhs * rhs
}
static func * (lhs: MyNumber, rhs: MyNumber) -> MyNumber {
MyNumber(secret: Array(repeating: "", count: lhs.number * rhs.number), isNegative: lhs.isNegative != rhs.isNegative)
}
init(integerLiteral value: Int) {
let int = value
isNegative = int < 0
secret = Array(repeating: "", count: abs(int))
}
static func < (lhs: MyNumber, rhs: MyNumber) -> Bool {
lhs.number < rhs.number
}
init?<T>(exactly source: T) where T : BinaryInteger {
guard let int = Int(exactly: source) else {
return nil
}
self.init(integerLiteral: int)
}
var magnitude: MyNumber {
MyNumber(secret: secret, isNegative: false)
}
static func - (lhs: MyNumber, rhs: MyNumber) -> MyNumber {
if lhs < rhs {
return -(rhs - lhs)
} else {
return MyNumber(secret: Array(repeating: "", count: lhs.number - rhs.number))
}
}
prefix static func - (operand: MyNumber) -> MyNumber {
MyNumber(secret: operand.secret, isNegative: !operand.isNegative)
}
mutating func negate() {
isNegative.toggle()
}
init(secret: [String], isNegative: Bool = false) {
self.secret = secret
self.isNegative = isNegative
}
typealias Magnitude = MyNumber
static func + (lhs: MyNumber, rhs: MyNumber) -> MyNumber {
MyNumber(secret: lhs.secret + rhs.secret)
}
typealias IntegerLiteralType = Int
}
How on earth is <insert whoever is doing the conversion here> going to know that, to convert MyNumber
to Float
, it has to do Float(aMyNumber.secret.count)
!? Not to mention that secret
is private.
On the other hand, Float.init
does know how to convert BinaryFloatingPoint
s and BinaryInteger
s to Float
, because those protocols define the necessary properties such that Float.init
can understand how they are represented. For example, BinaryInteger
s are represented by words
, which is a RandomAccessCollection
of UInt
s, indexed by Int
s.
I suggest that you only add the percent
method to types of Series
where it actually makes sense:
extension Series where T : BinaryInteger {
func percent(value: T) -> Float {
let v: Float = Float(value)
let m: Float = Float(minValue)
let r: Float = Float(range)
return (v - m) / r
}
}
IMO, it would make sense for any T : FloatingPoint
, not just T : BinaryFloatingPoint
:
extension Series where T : FloatingPoint {
func percent(value: T) -> T {
let v = value
let m = minValue
let r = range
return (v - m) / r
}
}
Since FloatingPoint
supports division too, you don't need to convert to Float
!
How to implement generic function for adding both numbers and String in Swift?
One possible solution is this. You know you want your method to be applicable to anything that has a +
defined... So define an explicit protocol. Let's call it Addable
:
protocol Addable {
static func +(lhs: Self, rhs: Self) -> Self
}
Now, using extensions, declare conformance to Addable
for the types you care about:
extension String: Addable {}
extension Int: Addable {}
extension Double: Addable {}
And define your add
function as:
func add<T: Addable>(first: T, second: T) -> T {
return first + second
}
Swift 5: Merging Two Generic Functions with T: BinaryInteger & T:BinaryFloatingPoint with Output Type Specified
If I understand you correctly, you just want formatArrayDataforImage
to accept any array of BinaryInteger or BinaryFloatingPoint without rewriting the whole thing.
To do that, write it for floating point, and then call that from the integer version.
I believe this is your floating point version:
func formatArrayDataforImage<Element>(dataSet: [Element]) -> [UInt8]
where Element: BinaryFloatingPoint {
guard let max = dataSet.max() else { fatalError("Find max fail.")}
guard let min = dataSet.min() else { fatalError("find min fail.")}
// Note this is a little dangerous since it it crash if
// min and max aren't in the range 0 - 1. I'd probably add
// assertions at least.
return dataSet.map { UInt8(255 * ($0 - min) / max) }
}
There's no particular reason to convert this to Float. It works fine for any BinaryFloatingPoint.
And then to call it for a BinaryInteger, you would just need to map the values to a floating point type (like Float), like you're currently doing.
func formatArrayDataforImage<Element>(dataSet: [Element]) -> [UInt8]
where Element: BinaryInteger // <=== Note different `where` clause
{
// Since this creates a [Float] it will call the other function
formatArrayDataforImage(dataSet: dataSet.map(Float.init))
}
As a note, your concurrentPerform is trying to do an illegal operation in parallel (you cannot modify an array on multiple threads simultaneously, not even if you are modifying different indexes). It turns out not to matter because your synchronization just makes the concurrentPerform serial again. All that code is equivalent to my dataSet.map(Float.init)
.
Related Topics
Cocos2D Fcm Push Notification Not Working
Weak Reference Implementing a (Generic) Protocol
Convert Integer to Array of UInt8 Units
iOS Application Support Directory Exists on Devices by Default
Add Accessory to Home Always Returns Error(Genericerror)
Function Inside Prepareforsegue Will Not Execute
What's The How to Access a Swift Package Item from Objective-C
Core Image and Memory Leak, Swift 3.0
Populating Collection View from Array in Document Directory
Dynamic UIcollectionviewcell Width with Respect to Frame Width and Aspect Ratio
Downloading PDF Using Nsurlsession (Http Post) with Parameters
How to Download Video Urlstring from Firebase Database Not Storage in Swift
How to Instantiate a View Controller Programatically, Without Storyboard
Post Urlrequest Doesn't Work in Swift 4
How to Make Phone Calls in Swift