必须用 decimal(m,d) 显式声明变量和参数,禁用 float/double;调用时传 100.0 而非 100,避免隐式转 double 导致精度丢失。

直接用 DECIMAL 类型定义变量和参数,别碰 FLOAT 或 DOUBLE —— 这是唯一能根治函数内精度丢失的做法。
存储过程里声明变量必须带精度和标度
MySQL 不允许只写 DECIMAL,漏掉 (M,D) 会报 ERROR 1064。哪怕你只是临时存个金额,也得明确说清总位数和小数位数。
-
DECLARE amount DECIMAL(15,2);✅ 安全,覆盖亿元级金额+分 -
DECLARE rate DECIMAL(12,6);✅ 汇率/利率常用,保留六位小数 -
DECLARE total DECIMAL;❌ 语法错误 -
DECLARE total DECIMAL(10);❌ 缺少标度,仍报错
标度 D 不能为负,且必须 ≤ M;赋值超限时 MySQL 默认四舍五入截断(如 DECIMAL(5,3) 存 123.4567 → 123.457),不是报错,容易被忽略。
调用时传裸数字会隐式转成 DOUBLE
哪怕参数声明是 DECIMAL(15,2),只要调用时传 100 或 0.08 这种字面量,MySQL 就按 DOUBLE 解析,中间一算就失准。
-
CALL calc(100.0, 0.08);✅ 末尾加.0是最轻量的提示方式 -
CALL calc(CAST(100 AS DECIMAL(10,2)), CAST(0.08 AS DECIMAL(5,4)));✅ 最稳妥,强制类型对齐 -
CALL calc(100, 0.08);❌0.08被当DOUBLE,乘法结果大概率变成DOUBLE类型
执行前可查 SELECT @@sql_mode;,确认没开 ALLOW_INVALID_DATES 等宽松模式,否则隐式转换更难察觉。
函数内所有运算都要显式保 DECIMAL 链路
SUM()、ROUND()、TRUNCATE() 对 DECIMAL 有效,但类型推导不总如预期:两个 DECIMAL(10,2) 相乘,结果是 DECIMAL(20,4);相加是 DECIMAL(11,2)。若你把结果塞进一个窄的变量(如 DECIMAL(10,2)),可能静默截断。
-
SET @res = CAST(100 AS DECIMAL(10,2)) / CAST(3 AS DECIMAL(10,2));✅ 结果仍是DECIMAL -
SET @res = 100 / 3;❌ 返回DOUBLE,值约33.333333333333336 -
SELECT ROUND(0.1 + 0.2, 2);✅ 显示层补救,但底层仍是浮点误差再修 -
SELECT CAST(0.1 AS DECIMAL(10,2)) + CAST(0.2 AS DECIMAL(10,2));✅ 从源头掐断误差
常量参与计算时,100.0 比 100 更安全;除法尤其危险,不显式转就掉坑里。
真正容易被忽略的是:类型链一旦断裂(比如某个中间值被当成 DOUBLE),后续所有计算都跟着漂移,而 MySQL 不报错、不警告——它只是默默给你一个“看起来对”的错值。











