JavaScript的Number採用64位元的IEEE 754標準來表示整數和浮點數數值,其中整數的安全範圍在-253 - 1到253 - 1之間。換句話說,Node.js既不能直接使用到32位元的整數,同時也無法使用64位元的整數。因此,如果要拿Node.js來做一些稍微複雜的計算,就需要撰寫額外的程式來處理資料型態的部份。像是32位元整數經常會遇到的「溢位」,想要在Node.js上重現就比較麻煩。
以下C語言程式示範了32位元的整數遇到溢位的情形:
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char **argv) {
int32_t a = 2147483647;
a *= 2;
printf("%d\n", a);
return 0;
}
輸出結果為:
32位元的整數所能表示的最大值為231 - 1 = 214748364710 = 011111111111111111111111111111112
。程式第5行宣告了變數a。並指派了一個最大值給變數a儲存。接著在程式第6行讓a儲存的數值乘2了。如果熟悉位元運算的話,可以很快地將「乘2」這個動作對應成向左位移1個位元,也就是說111111111111111111111111111111112
向左位移1個位元之後,原本在最左邊的最大位元(MSB)0
會因為左移而被移除,而最小位元(LSB)會被填上0
,所以位移後最終的數值是111111111111111111111111111111102
。由於有號數是使用2的補數來表示,因此111111111111111111111111111111102
換算成10進制數值後會是-210
。若要使用JavaScript來得到像上述這樣進行乘法計算卻遇到溢位又要考慮補數的結果,可以利用Math所提供的imul
函數來完成。
console.log(Math.imul(2147483647, 2));
輸出結果為:
然而網路上還流傳著一種做法,那就是將最後的運算結果去OR一個0
。程式如下:
console.log((2147483647 * 2) | 0);
輸出結果為:
這裡必須要注意到的是,「將最後的運算結果去OR一個0
」雖然在上面的範例中算出來的答案和Math所提供的imul
函數相同,但它並不是一個正確的32位元整數的解決方案,因為它在很多情況下算出來的答案不是對的。舉例:
console.log(Math.imul(1412337538, 515419545));
console.log((1412337538 * 515419545) | 0);
輸出結果為:
675192448
使用以下C程式來驗證結果:
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char **argv) {
int32_t a = 1412337538;
a *= 515419545;
printf("%d\n", a);
return 0;
}
輸出結果為:
既然JavaScript可以直接使用Math所提供的imul
函數來模擬32位元整數的乘法,想必加、減、除也有對應的函數囉?很遺憾,加、減、除法並沒有這樣的函數可以使用,所以說在JavaScript上要進行32位元整數運算是一件麻煩的事情。利用C/C++或是Rust語言實作的函數庫來進行32位元的整數運算才會容易許多。
int32
int32
是一個使用Rust函式庫開發的Node.js的模組,使用Rust程式語言原生的u32
和i32
型別來直接進行32位元的整數運算。
npmjs.com
npm 安裝指令
使用方法
add
import { add } from "int32";
const n = add(1, 2); // 3
subtract
import { subtract } from "int32";
const n = subtract(1, 2); // -1
multiply
import { multiply } from "int32";
const n = multiply(2, 6); // 12
divide
import { divide } from "int32";
const n = divide(6, 4); // 1
pow
import { pow } from "int32";
const n = pow(2, 3); // 8
shiftLeft
import { shiftLeft } from "int32";
const n = shiftLeft(5, 2); // 20
shiftRight
import { shiftRight } from "int32";
const n1 = shiftRight(5, 2); // 1
const n2 = shiftRight(6, 1); // 3
const n3 = shiftRight(-5, 1); // -3
shiftRightUnsigned
import { shiftRightUnsigned } from "int32";
const n = shiftRightUnsigned(-5, 1); // 2147483645
rotateLeft
import { rotateLeft } from "int32";
const n = rotateLeft(0b10000000000000000000000100000000, 1); // 0b00000000000000000000001000000001
rotateRight
import { rotateRight } from "int32";
const n = rotateRight(0b00000000000000000000000100000001, 8); // 0b00000001000000000000000000000001