Reputation: 215
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
Reputation: 73186
<<
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
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
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
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