Currently working on an Elixir project which has a need for the standard javascript method Math.imul functionality. After implementing the method in Elixir, the returned value is vastly different from that of the one produced by Javascript's Math.imul (via the shim here which I'm referencing for the recreation) which is an obvious problem if I'm going for value parity. My goal is for the Elixir function to produce the same value as the Javascript one. It appears that all of the values produced within both methods are identical right up until the final result calculation utilizing the bitwise shift left operator.
imul(-5, 12) produces...
Javascript:
const imul = (x: number, y: number) => {
const a = ToUint32(x);
// 4294967291
const b = ToUint32(y);
// 12
const ah = (a >>> 16) & 0xffff;
// 65535
const al = a & 0xffff;
// 65531
const bh = (b >>> 16) & 0xffff;
// 0
const bl = b & 0xffff;
// 12
return (al * bl) + (((ah * bl) + (al * bh)) << 16)
// -60
}
Elixir:
def imul(x, y) do
a = toUInt32(x)
# 4294967291
b = toUInt32(y)
# 12
ah = (a >>> 16) &&& 0xffff
# 65535
al = a &&& 0xffff
# 65531
bh = (b >>> 16) &&& 0xffff
# 0
bl = b &&& 0xffff
# 12
(al * bl) + (((ah * bl) + (al * bh)) <<< 16)
# 51539607492
end
def toUInt32(num) do
<<num :: integer-unsigned-32>>
|> :binary.decode_unsigned(:big)
end
I understand somewhat that Elixir operates on integers of arbitrary precision, so you don't need to worry about 32-bit integer limits as in JavaScript. I believe the semantics of bitwise operations are, however, similar in terms of manipulating individual bits so I'm slightly confused at what I'm doing wrong here and what I need to do to arrive at the same value that the canonical (Math.imul) method is arriving at, -60. Help appreciated!
As Elixir doesn't have integer overflows, you don't have to worry about bitshifts and can simply multiply the values, then convert to a signed integer.
More detailed explanation:
Javascript:
Elixir:
You can see that in Javascript, the result of a bitwise operation is assumed to be a 32-bit signed integer, so bitshifting left causes an integer overflow. In Elixir it is an unlimited-bit signed integer, so bitshifting left does not cause an integer overflow.
If you want to reproduce the same behaviour as Javascript, you have to use the binary special form to tell Elixir to convert the left-shifted number to a 32-bit signed integer:
But as shown above, the bit-shifting is not necessary in Elixir in the first place.