HOWTO · JavaScript

JavaScript 中的整數除法

在 JavaScript 中選擇 Math.trunc 或 Math.floor,讓負數情況下的商與餘數也能正確計算。

本頁內容

JavaScript 沒有 // 整數除法運算子。對一般 Number 值而言,若商應朝零捨去小數部分,請使用 Math.trunc(dividend / divisor),餘數則用 %。只有確實需要向下取整時才選擇 Math.floor()。負數時兩者不同,因此先決定規則比選擇簡寫更重要。

直接答案:選擇截斷或向下取整語意

整數除法可能表示兩種規則。截斷商會移除小數位:Math.trunc(-13 / 5) 為 -2。向下取整商朝負無限大捨入:Math.floor(-13 / 5) 為 -3。正數輸入時二者都是 2,差異可能直到生產環境出現負數才顯現。

當任務要把帶符號的量拆成商和帶符號餘數時使用截斷。對從零開始分桶或循環索引等商必須一直向下取整的工作,使用向下除法。JavaScript 的 % 回傳餘數,不是永遠非負的數學模;其符號跟隨被除數。MDN 說明餘數運算子與數學模的差別。

建議的 Number 商與餘數

對有限的 Number 輸入,Math.trunc 與 % 清楚表達商/餘數關係,這是一般整數除法的建議方法。計算前請檢查除數不是零:非零 Number 除以帶符號零會產生帶符號的 Infinity,0 / 0 是 NaN,除數為零的 Number 餘數也同樣是 NaN。ECMAScript 規範定義了這些除法與餘數情況。

const dividend = 13;
const divisor = 5;

console.log(`quotient: ${Math.trunc(dividend / divisor)}`);
console.log(`remainder: ${dividend % divisor}`);
quotient: 2
remainder: 3

餘數也可協助驗證結果:對整數輸入,dividend 等於 divisor * quotient + remainder。此關係有助於驗證,但直接以 Math.trunc 計算商,比間接推導更清楚地表達所需捨入規則。

負運算元與帶符號餘數

負運算元是截斷與向下取整語意的分界點。對 -13 / 5,截斷得到 -2,向下除法得到 -3。% 的結果為 -3,因為 JavaScript 對非零餘數保留被除數符號。兩種運算都不會因負輸入失敗;它們回答的問題不同。

const dividend = -13;
const divisor = 5;

console.log(`truncating quotient: ${Math.trunc(dividend / divisor)}`);
console.log(`floor quotient: ${Math.floor(dividend / divisor)}`);
console.log(`remainder: ${dividend % divisor}`);
truncating quotient: -2
floor quotient: -3
remainder: -3

需要向下除法或模時

若應用在正除數下需要非負模,請規範化餘數,而不要假設 % 已具備該行為。搭配 Math.floor,使兩個值使用相同的向下除法約定。下例適用於五項循環,不適用於每一種商/餘數工作。

const dividend = -13;
const divisor = 5;
const quotient = Math.floor(dividend / divisor);
const modulo = ((dividend % divisor) + divisor) % divisor;

console.log(`floor quotient: ${quotient}`);
console.log(`non-negative modulo: ${modulo}`);
floor quotient: -3
non-negative modulo: 2

此規範化假設除數為正。除數可能為負時,請另行定義並測試所需約定。

使用 BigInt 處理大型精確整數

Number 使用 IEEE-754 浮點值。在安全整數範圍之外,它無法精確表示每個整數,因此依賴 Number 的商或餘數前,應以 Number.isSafeInteger 驗證精確整數輸入。較大的精確整數請一致使用 BigInt。BigInt 除法朝零截斷,餘數也遵循同樣的帶符號餘數概念。

const dividend = 13n;
const divisor = 5n;

console.log(`quotient: ${dividend / divisor}`);
console.log(`remainder: ${dividend % divisor}`);

try {
  13n / 0n;
} catch (error) {
  console.log(`zero divisor: ${error.name}`);
}

try {
  13n / 5;
} catch (error) {
  console.log(`mixed types: ${error.name}`);
}
quotient: 2
remainder: 3
zero divisor: RangeError
mixed types: TypeError

不要混用 Number 和 BigInt 運算元;算術前應刻意轉換。BigInt 的零除數會擲出 RangeError,不同於 Number 的零除數,因此兩種情況都值得驗證輸入。MDN 記錄 BigInt 除法、混合型別錯誤及零除數行為。

為何不用位元強制轉換或 parseInt

你可能見過以 ~~value 或 value | 0 作為精簡的截斷慣用法。不要把它們用於一般整數除法:位元運算子會強制轉換為帶符號 32 位元整數,因而改變範圍外的值。parseInt 用於剖析字串,不是數值捨入 API。MDN 說明 Math.trunc 與位元轉換的差異。下方對照展示 32 位元強制轉換邊界與正確的字串剖析行為。

const value = 2 ** 32 + 1;

console.log(`Math.trunc: ${Math.trunc(value)}`);
console.log(`~~value: ${~~value}`);
console.log(`value | 0: ${value | 0}`);
console.log(`parseInt("13.8", 10): ${parseInt("13.8", 10)}`);
Math.trunc: 4294967297
~~value: 1
value | 0: 1
parseInt("13.8", 10): 13

對數值 Number 使用 Math.trunc,只有輸入是待剖析文字時才用 parseInt(text, 10)。兩種簡寫都不能修復浮點精度;需要精確的大整數算術時請使用 BigInt。

決策摘要與限制

一般 JavaScript 整數除法使用 Math.trunc(a / b) 與 a % b。只有需要向下取整語意時,才用帶規範化模的 Math.floor(a / b)。精確大整數使用 BigInt,拒絕零除數,且不要在一次運算中混用 BigInt 與 Number。不要將位元強制轉換或 parseInt 當成通用替代方案,因為它們解決的是不同問題。