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

SQL Server 四捨五入函數深度解析:ROUND、CEILING、FLOOR 在不同資料類型下的行為差異與避坑指南

     編輯:關於SqlServer

前言:看似簡單的四捨五入,暗藏資料類型陷阱

報表數字與應用計算不一致、匯入匯出金額對不上、稅務計算差異……許多「離奇」Bug 根源於 **資料類型 (int, decimal, float) × 四捨五入函數 (ROUND, CEILING, FLOOR) × 精度參數** 的組合炸彈。本文透過系統性實測案例,建立精確的心智模型。

三大函數語意定義

函數語意關鍵字
ROUND(value, length [,function])四捨五入至指定精度 (length>0 為小數位,<0 為整數位)。第三參數 function=1 為截斷。銀行家算法 (Banker's Rounding) 視實作而定
CEILING(value)返回 ≥ value 的最小整數 (向上取整,負數向零方向)。天花板
FLOOR(value)返回 ≤ value 的最大整數 (向下取整,負數遠離零方向)。地板

實測案例矩陣:資料類型決定命運

案例 1:整數 - 三函數恆等

DECLARE @v INT = 6;     SELECT ROUND(@v,1), CEILING(@v), FLOOR(@v);  -- 6, 6, 6
DECLARE @v INT = -11;   SELECT ROUND(@v,1), CEILING(@v), FLOOR(@v);  -- -11, -11, -11

整數無小數位,三函數皆返回原值。

案例 2:隱含轉換陷阱 - INT 變數存放小數常值

DECLARE @v INT = -11.5;  -- 隱含轉換為 -11 (截斷小數)!
SELECT ROUND(@v,2), CEILING(@v), FLOOR(@v);  -- -11, -11, -11

**關鍵教訓:** 變數宣告類型決定儲存值,**小數常值指派給 INT 會靜默截斷**,非四捨五入!

案例 3:DECIMAL(10,2) - 精度參數主導 ROUND 結果


DECLARE @v DECIMAL(10,2) = 11.05;
SELECT ROUND(@v,1), ROUND(@v,2), ROUND(@v,3), CEILING(@v), FLOOR(@v);
-- 11.10 | 11.05 | 11.05 | 12 | 11

DECLARE @v DECIMAL(10,2) = -14.46;
SELECT ROUND(@v,1), ROUND(@v,2), ROUND(@v,3), CEILING(@v), FLOOR(@v);
-- -14.50 | -14.46 | -14.46 | -14 | -15
  • ROUND(@v,1) 看第 2 位小數 (5/6) 決定進位。
  • CEILING/FLOOR 作用於**完整數值**,忽略精度參數,負數方向相反。

案例 4:高精度 DECIMAL(10,10) - 精度參數逐位遞增效果


DECLARE @v DECIMAL(10,10) = 0.5432167890;
SELECT ROUND(@v,1), ROUND(@v,2), ROUND(@v,3), ROUND(@v,4), ROUND(@v,5),
       ROUND(@v,6), ROUND(@v,7), ROUND(@v,8), ROUND(@v,9), ROUND(@v,10),
       CEILING(@v), FLOOR(@v);
-- 0.5 | 0.54 | 0.543 | 0.5432 | 0.54322 | 0.543217 | 0.5432168 | 0.54321679 | 0.543216789 | 0.543216789 | 1 | 0

精度參數超過實際小數位時,結果穩定不再變化。

案例 5:FLOAT(10) - 浮點數近似本質導致不可預測


DECLARE @v FLOAT(10) = 0.1234567890;
SELECT ROUND(@v,1), ROUND(@v,2), ROUND(@v,3), ROUND(@v,4), ROUND(@v,5),
       ROUND(@v,6), ROUND(@v,7), ROUND(@v,8), ROUND(@v,9), ROUND(@v,10),
       CEILING(@v), FLOOR(@v);
-- 0.1 | 0.12 | 0.123 | 0.1235 | 0.12346 | 0.123457 | 0.1234568 | 0.12345679 | 0.123456791 | 0.123456791 | 1 | 0

警告: `FLOAT`/`REAL` 為 IEEE 754 二進制浮點,**0.1 無法精確表示**,累加運算會產生誤差,**嚴禁用於財務/精確計算**。

避坑指南與最佳實務

1. 財務/金額/精確計算:唯一正解 = DECIMAL(p,s)

  • 明確宣告精度 `DECIMAL(19,4)` (支援萬億級、4 位小數)。
  • 使用 `ROUND(@amt, 2)` 標準四捨五入 (或銀行家算法需自行實作)。
  • **禁用 FLOAT/REAL/MONEY/SMALLMONEY** (MONEY 內部為整數縮放,除法會截斷精度)。

2. 統計/科學計算/近似值:FLOAT 可接受,但需容忍誤差

  • 使用 `ROUND(@val, 6)` 等固定精度輸出。
  • 比較浮點數相等時,**永不使用 `=`**,改用 `ABS(a-b) < 0.000001`。

3. 截斷而非四捨五入:ROUND 第三參數

SELECT ROUND(123.456, 2, 1);  -- 123.45 (截斷)
SELECT ROUND(123.456, 2, 0);  -- 123.46 (四捨五入,預設)

4. 負數 CEILING/FLOOR 方向記憶法

  • CEILING (天花板) = 向上 = 朝向 +∞:-14.46 → -14 (較大/接近零)。
  • FLOOR (地板) = 向下 = 朝向 -∞:-14.46 → -15 (較小/遠離零)。

5. 測試驗證腳本:建立你的專屬行為備忘錄


CREATE TABLE #RoundingTest (TypeName VARCHAR(20), Val SQL_VARIANT);
INSERT #RoundingTest VALUES
('INT', 6), ('INT', -11),
('DECIMAL(10,2)', 11.05), ('DECIMAL(10,2)', -14.46),
('DECIMAL(10,10)', 0.5432167890),
('FLOAT', 0.1234567890);

SELECT TypeName, Val,
       ROUND(CAST(Val AS DECIMAL(38,10)), 2) AS Round2,
       CEILING(CAST(Val AS DECIMAL(38,10))) AS Ceil,
       FLOOR(CAST(Val AS DECIMAL(38,10))) AS Floor
FROM #RoundingTest;

結語

四捨五入不是數學課本上的單一真理,而是 **資料類型 × 函數語意 × 參數設定** 的工程決策。**明確宣告 DECIMAL、統一使用 ROUND(length, 0)、禁用 FLOAT 處理金額、測試邊界值**,是避免「分不清」的唯一鋼鐵律。

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