先放一个最直接的结果:如果你在 ABAP 里用 TYPE f 接 0.1 + 0.2,再拿它和字面量 0.3 做等值判断,答案大概率是 not equal,误差大约在 5.55e-17 这个量级。这个问题我在不少项目里见过——不是写错语法,不是数据库字段精度奇怪,而是最早建模时就用错了数值类型,把本应该存成 CURR、DEC 或 DECFLOAT34 的金额/数量字段,做成了一个 FLTP(二进制浮点)字段,于是一路从内表、报表、ALV 合计、OData 响应传到前端,最终在某一个 UI5 表格里露出 0.30000000000000004 的尾巴。
很多从 Java、C# 转过来写 ABAP 的人会觉得“这是经典 JS 问题”,其实 ABAP 里的 TYPE F 遵循的也是同一套 IEEE 754 双精度规则。区别在于,ABAP 还有定点类型 P 和十进制浮点类型 DECFLOAT16/34,可以做业务级规避;而 OData 和 RAP 项目里,如果 CDS 视图的字段类型没钉对,问题就会从后端模型一路传染到前端。这篇文章不打算只讲原理,还会给出现场能直接用的代码习惯和工程解法。
1. 一个最小可复现 DEMO 与三个真实翻车现场
很多同事第一次在 ABAP 里看到这个现象时,第一反应是“ABAP 应该把浮点处理好了吧”。其实没有。给一段最简化的复现代码:
abap复制DATA: lv_f1 TYPE f,
lv_f2 TYPE f,
lv_sum TYPE f.
lv_f1 = '0.1'.
lv_f2 = '0.2'.
lv_sum = lv_f1 + lv_f2.
IF lv_sum = '0.3'.
WRITE: / 'equal'.
ELSE.
WRITE: / 'not equal'.
DATA(lv_diff) = lv_sum - '0.3'.
WRITE: / 'diff =', lv_diff DECIMALS 20.
ENDIF.
如果你在系统里只看到 not equal,但 WRITE 显示那一行仍然是 0.3,不要觉得奇怪,那是输出工具帮你做了格式化,判断分支里的真实内存值和字面量 0.3 并不相等。想验证的人,可以再把 lv_sum 赋值给一个 P
