Pat
Pat

Reputation: 325

Make all values of an array negative or change sign

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

Answers (3)

dfrib
dfrib

Reputation: 73196

The 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

Martin R
Martin R

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

Miknash
Miknash

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

Related Questions