何をしたか
よくありそうな除算の結果を少数切り捨てして、0除算の場合は任意の値(例えばゼロとかエラー文字列)を返す式を短く書く方法を研究した結果こうなりました。
function divide(a, b) {
return a / b | 0;
}
論理和(||
)じゃなくてビット論理和( |
)です
何故こうなるのか
JavaScriptにおいて数値は内部的に64ビットの浮動小数点数(IEEE 754形式)として扱われるが、ビット演算は32ビット整数で行われるため、この過程で浮動小数点数から整数への変換が行われます。
またJavaScriptでは、a / b
のbが0の場合、結果はInfinity
または-Infinity
になります。これらは浮動小数点数の表現であり、次のビットパターンで表されます。
Infinity
の64ビット二進数表現の変換過程
0111111111110000000000000000000000000000000000000000000000000000
上位32ビット(切り捨てられる部分):
01111111111100000000000000000000
下位32ビット(残される部分):
00000000000000000000000000000000
-Infinity
の64ビット二進数表現の変換過程
1111111111110000000000000000000000000000000000000000000000000000
上位32ビット(切り捨てられる部分):
11111111111100000000000000000000
下位32ビット(残される部分):
00000000000000000000000000000000
変換過程でInfinity
や-Infinity
は、IEEE754形式の浮動小数点数で特定のビットパターンを持ちます。しかし、これらの値を32ビット整数に変換する際にビット情報が失われ、「丸め込まれる」結果となり0になります。
結局のところ実用的じゃないとは思う
ビット演算はMath.floor()
など関数用いるよりパフォーマンスに優れるという情報も見かけましたが未検証です。
ここまで調べてみて非常に興味深い挙動だったのですが、多分一般的で無さすぎて可読性が劣ってしまいます。
あと32ビット整数が表現できるのは、符号付き整数として最大約±2.1億(具体的には-2,147,483,648から2,147,483,647まで)の範囲で、これを考慮していると短く書こうとした目的から外れてしまう結果になる気がします。
> 2147483648|0
-2147483648
実用に用いるのはやめて無難にこんな感じにします…
function divide(a, b) {
const result = a / b;
if(Number.isFinite(result)) {
// エラーハンドリング
}
return Math.floor(result);
}