Swift - Multiplying each Element of an Array by 2

I want to multiply each element of my code by 2. Here is my code:

var arr = [5, 77, 34, -22]

for x in arr {

    arr[arr.index(of: x)!] = x * 2

}

print(arr)

This prints the following on the console:

[10, 154, 68, -44]

When I add 1 to the array, I have the following code:

var arr = [5, 77, 34, -22, 1]

for x in arr {

    arr[arr.index(of: x)!] = x * 2

}

print(arr)

As expected, the result is the following:

[10, 154, 68, -44, 2]

However, when I add 2 to the array using the following code:

var arr = [5, 77, 34, -22, 1, 2]

for x in arr {

    arr[arr.index(of: x)!] = x * 2

}

print(arr)

I get a bizarre result:

[10, 154, 68, -44, 4, 2]

Why is this the case? I used Playground for all these results. Any explanation would be much appreciated, thanks in advance.

Upvotes: 0

Views: 3064

Answers (4)

dfrib
dfrib

Reputation: 73186

Alternative for the special case of multiplication by 2 (or powers of it): bitwise left shift <<

Since integers are stored in binary format, a multiplication by 2 (the base of binary) is equivalent to a single bitwise left shift << applied to each number.

var arr = [5, 77, 34, -22, 1]
arr = arr.map{ $0 << 1 }
print(arr) // [10, 154, 68, -44, 2]

I would suspect that this approach have a better performance than the explicit multiplication by 2 (as shown in the other answers), but at the cost of readability/semantics/intent. Unless you're working on a HPC application, the latter should be valued, and the explicit multiplication preferred.


A note on value overflow

It's worth noting that for a value that is too large to safely enter the multiplication operation (say value > Int.max / 2), the explicit multiplication by 2 will yield a value overflow, yielding a runtime exception if the * operator is used

var arr = [5, 77, 34, -22, Int.max]
arr = arr.map{ $0 * 2 } // runtime exception

Whereas the bitwise left shift approach above will avoid the runtime exception by silently discarding any overflow that occurs during the operation

var arr = [5, 77, 34, -22, Int.max]
arr = arr.map{ $0 << 1 }
print(arr) // [10, 154, 68, -44, -2]

The same discarding is used also when using the masking multiplication operator (&*) for the multiplication:

var arr = [5, 77, 34, -22, Int.max]
arr = arr.map{ $0 &* 2 }
print(arr) // [10, 154, 68, -44, -2]

To understand how the bitwise truncation/overflow discarding ends up in value -2 for a signed type as Int, it's easier to study a smaller type, say Int8, in which case the discarding of the overflow becomes apparrent.

var arr: [Int8] = [5, 77, 34, -22, Int8.max] // allowed values: -128...127
arr = arr.map{ $0 << 1 }
print(arr) // [10, 154, 68, -44, -2]
    // Int8.max      = 127,  0b1111_1111 (unsigned)
    // Int8.min      = -128, 0b0000_0000 (unsigned)
    // Int8.max << 2 = 0b1111_1111 << 1 = { discard overflow } 
    //               = 0b1111_1110 = -2 (unsigned)

print(Int8(bitPattern: 0b1111_1110)) // -2

Upvotes: 2

PGDev
PGDev

Reputation: 24341

Instead of using for loop, you can use map method on array.

    var arr = [5, 77, 34, -22, 1, 2]
    arr = arr.map {
        return $0 * 2
    }
    print(arr)

Output:

[10, 154, 68, -44, 2, 4]

For more on map method refer to: https://developer.apple.com/reference/swift/dictionary/1689397-map

Upvotes: 1

Rahul
Rahul

Reputation: 2100

First of all the above way of multiplying element is not a good idea.

Why?

because if the number is repeated you will apply the multiplication operation to the element which comes first.

 var arr = [1, 2] // result will be [4, 2] 

 // This not expected, you need
 [2, 4]

This is what happens in your case. Here is the STDOUT of array for each iteration

[5, 77, 34, -22, 1, 2]
[10, 77, 34, -22, 1, 2]
[10, 154, 34, -22, 1, 2]
[10, 154, 68, -22, 1, 2]
[10, 154, 68, -44, 1, 2]
[10, 154, 68, -44, 2, 2]
[10, 154, 68, -44, 4, 2]

In the second last state of the array, you can see you got two consecutive 2 and when you call index: of it return the index of the first element it finds in sequence.

A better way to do this would be by using enumration or map.

  for (index, value) in arr.enumerated() {
    arr[index] = value * 2
  }

  // or
  let array = arr.map {$0 * 2}

Upvotes: 2

Taras Chernyshenko
Taras Chernyshenko

Reputation: 2829

var arr: [Int] = [5, 77, 34, -22, 1, 2]

for x in arr {
    let i = arr.index(of: x)!
    print(i)
    arr[i] = x * 2
}

You have this issue because you re-write arr on a each iteration of for in loop. That's why, when you came on last iteration step(arr.index(of: 2)!), you array has next view: [10, 154, 68, -44, 2, 2].

According to documentation arr.index(of: x)! returns the first index where the specified value appears in the collection., so for last step i will be 4, not 5

Upvotes: 1

Related Questions