首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

取模與取餘運算深度解析:定義差異、語言行為與數學本質

     編輯:關於C語言

Modulo vs Remainder:為什麼 -8 % 3 在 C/Python 中結果不同?

「取模」與「取餘」在數學定義、程式語言實作、硬體指令層面均有差異。混淆兩者會導致負數運算邏輯錯誤、雜湊分佈不均、環狀緩衝區索引越界等難以察覺的 Bug。本文從數學定義出發,對比主流語言行為,給出跨語言移植的安全守則。

數學定義

給定整數 a (被除數)、b ≠ 0 (除數),存在唯一整數商 q 與餘數 r 滿足:

a = b * q + r

差異在於 商 q 的取整方向:

運算類型商 q 取整規則餘數 r 範圍數學符號
取餘向零截斷 (Truncation, fix())|r| < |b|,sign(r) = sign(a)a rem b
取模向下取整 (Floor, floor())0 ≤ r < |b| (b>0),sign(r) = sign(b)a mod b

範例:-8 ÷ 3

a = -8, b = 3
真商 = -2.666...

取餘: q = trunc(-2.666) = -2
      r = a - b*q = -8 - 3*(-2) = -2  (sign 同 a)

取模: q = floor(-2.666) = -3
      r = a - b*q = -8 - 3*(-3) = 1   (sign 同 b,且 0 ≤ r < 3)

主流語言行為對照表

語言運算子語意-8 % 38 % -3-8 % -3備註
C (C99+)%取餘-22-2C89 未定義負數行為
C++ (C++11+)%取餘-22-2同 C
Java%取餘-22-2Math.floorMod 為取模
C#%取餘-22-2Math.IEEERemainder 不同
Python%取模1-2-2divmod 回傳 (q, r) 取模語意
JavaScript%取餘-22-2ES2021 新增 Math.rem
Go%取餘-22-2
Rust%取餘-22-2.rem_euclid() 為取模
Ruby%取模1-2-2
PHP%取餘-22-2fmod 同取餘
Swift%取餘-22-2
SQL (標準)MOD()取模1-2-2各資料庫實作略有差異

關鍵推論

  • 同號 (a*b > 0):取模 = 取餘,結果相同。
  • 異號 (a*b < 0):結果不同。取模結果號同 b (非負);取餘結果號同 a。
  • 取模結果恆為非負 (當 b > 0),極適合陣列索引、雜湊桶位、環狀緩衝區:index = ((key % N) + N) % N (取餘語言) vs index = key % N (取模語言)。

跨語言安全移植守則

  1. 明確語意:程式碼註解標註「此處需取模/取餘語意」。
  2. 封裝函式:
    // C/C++/Java/C#/JS/Go: 取模 (非負結果)
    int mod(int a, int b) { int r = a % b; return r < 0 ? r + (b > 0 ? b : -b) : r; }
    
    // Python/Ruby: 取餘 (同號被除數)
    int rem(int a, int b) { int r = a % b; return (a < 0) != (b < 0) && r != 0 ? r - b : r; }
  3. 單元測試覆蓋負數邊界:(-8,3), (8,-3), (-8,-3), (0,5), (5,0)。
  4. 避免直接用 % 做索引:統一呼叫 mod()。

硬體層面

  • x86 IDIV 指令產生的餘數符號同被除數 (取餘語意)。
  • 編譯器若需取模語意,會在 IDIV 後插入修正指令 (測試餘數符號、條件加減除數)。

總結

取餘 = 向零截斷商,結果號同被除數 (C/C++/Java/C#/JS/Go 默認)。
取模 = 向下取整商,結果號同除數 (非負) (Python/Ruby/SQL 默認)。
遇到負數運算,**務必查閱該語言規格**,或封裝統一語意函式,切勿憑直覺假設行為。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved