Reputation: 325
In SWIFT, is there a quick and efficient way to make all values of an array change sign (positive values become negative and vice-versa) ?
For example:
let x: [Double] = [1.0, 2.0, -3.0, 4.0, -5.0]
Should become:
// x = [-1.0, -2.0, 3.0, -4.0, 5.0]
Upvotes: 3
Views: 2096
Reputation: 73196
negate()
method of FloatingPoint
If you choose to proceed with a non-Accelerate approach, then you might prefer (e.g. semantics) using the negate()
method of FloatingPoint
to negate the values of the array in-place.
var x = [1.0, 2.0, -3.0, 4.0, -5.0]
for i in x.indices { x[i].negate() }
print(x) // [-1.0, -2.0, 3.0, -4.0, 5.0]
In fact, the -
prefix operator from FloatingPoint
simply calls the negated()
method of FloatingPoint
, where the implementation of the latter simply makes use of the negate()
method to copy, negate and return and negated copy of self
.
extension FloatingPoint { // ... public func negated() -> Self { var rhs = self rhs.negate() return rhs } } // ... public prefix func - <T : FloatingPoint>(x: T) -> T { return x.negated() }
Finally, the negate()
method itself is implemented in the conformance of Double
(/Float
, Float80
, CGFloat
) to BinaryFloatingPoint
, calling the Builtint
function fneg_FPIEEE
on self
(source here
% for self_type in all_floating_point_types(): %{ Self = self_type.stdlib_name // ... extension ${Self}: BinaryFloatingPoint { // ... public mutating func negate() { _value = Builtin.fneg_FPIEEE${bits}(self._value) } } // ... }
I haven't found a reference for the implemention of the Builtin.fneg_FPIEEE
method (Floating Point IEEE), but most likely it simply flips the sign bit of self
.
Upvotes: 2
Reputation: 539965
If you need a fast solution for large arrays then you can use
cblas_dscal()
("scalar multiplication, double precision") from the Accelerate framework:
import Accelerate
var x = [1.0, 2.0, -3.0, 4.0, -5.0]
cblas_dscal(Int32(x.count), -1.0, &x, 1)
print(x) // [-1.0, -2.0, 3.0, -4.0, 5.0]
Remark: There is also cblas_sscal()
for Float
arrays ("single precision").
Performance comparison: Here is a quite simple performance comparison of the various methods:
let N = 1_000_000 // # of array elements
let R = 100 // # of repetitions
func test1() {
var x = (0..<N).map { _ in drand48() }
let start = Date()
for _ in 0..<R {
for i in x.indices { x[i] = -x[i] }
}
let time = Date().timeIntervalSince(start)
print("x[i] = -x[i] ", time)
}
func test2() {
var x = (0..<N).map { _ in drand48() }
let start = Date()
for _ in 0..<R {
for i in x.indices { x[i].negate() }
}
let time = Date().timeIntervalSince(start)
print("x[i].negate() ", time)
}
func test3() {
var x = (0..<N).map { _ in drand48() }
let start = Date()
for _ in 0..<R {
x = x.map { -$0 }
}
let time = Date().timeIntervalSince(start)
print("x.map { -$0 } ", time)
}
func test4() {
var x = (0..<N).map { _ in drand48() }
let start = Date()
for _ in 0..<R {
cblas_dscal(Int32(x.count), -1.0, &x, 1)
}
let time = Date().timeIntervalSince(start)
print("cblas_dscal ", time)
}
test1()
test2()
test3()
test4()
Results (on a 3.5 GHz Intel iMac, compiled and run in Release configuration:)
x[i] = -x[i] 0.0492849946022034 x[i].negate() 0.0635690093040466 x.map { -$0 } 0.285757005214691 cblas_dscal 0.0506410002708435
As it turns out, cblas_dscal()
has about the same speed as
an explicit loop with x[i] = -x[i]
or x[i].negate()
, but is
significantly faster than the map()
method.
Of course the results may be different on a different hardware, or with other array sizes.
As usual, start with the method which you are most familiar with and look for faster methods if it turns out to be performance-critical.
Upvotes: 5
Reputation: 7948
How about map:
let x: [Double] = [1.0, 2.0, -3.0, 4.0, -5.0]
let invertedX = x.map({$0 * (-1)})
result:
[-1.0, -2.0, 3.0, -4.0, 5.0]
Upvotes: 2