目前正在开发一个 Elixir 项目,该项目需要标准 javascript method
Math.imul
功能。在 Elixir 中实现该方法后,返回的值与 Javascript 的 Math.imul
生成的值有很大不同(通过我在此处引用的 shim 进行娱乐),如果我要这样做,这是一个明显的问题为了价值平价。我的目标是让 Elixir 函数产生与 Javascript 函数相同的值。看来两种方法中产生的所有值都是相同的,直到使用按位左移运算符计算最终结果。
imul(-5, 12)
产生...
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
}
灵丹妙药:
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
我知道 Elixir 对任意精度的整数进行操作,因此您不需要像 JavaScript 中那样担心 32 位整数限制。然而,我相信按位运算的语义在操作各个位方面是相似的,所以我对我在这里做错了什么以及我需要做什么才能达到与规范相同的值感到有点困惑(
Math.imul
)方法即将到达,-60
。感谢帮助!
Javascript:
> 1 << 30
1073741824
> 1 << 31
-214748364
灵丹妙药:
iex> 1 <<< 30
1073741824
iex> 1 <<< 31
2147483648
您可以看到,在 Javascript 中,按位运算的结果被假定为 32 位有符号整数,因此向左移位会导致整数溢出。在 Elixir 中,它是一个无限位有符号整数,因此向左移位不会导致整数溢出。
如果你想重现与Javascript相同的行为,你必须使用二进制特殊形式来告诉Elixir将左移数字转换为32位有符号整数:
iex> shifted_left = (ah * bl) + (al * bh) <<< 16
51538821120 # Equivalent to 101111111111111101000000000000000000, 36 bits
iex> <<shifted_left_with_integer_overflow::signed-32>> = <<shifted_left::32>>
<<255, 244, 0, 0>>
iex> (al * bl) + shifted_left_with_integer_overflow
-60