SQLite内存库中文本转Decimal求和结果异常 预期0却得极小非零值
问题原因解析
核心差异来自SQLite对DECIMAL类型的实现逻辑
- SQLite 没有内置的精确定点DECIMAL数据类型,你写的
CAST(xxx AS DECIMAL)语句在SQLite执行时,会直接把值转换为*8字节IEEE754双精度浮点数(REAL类型)*存储和运算,根本不会像SQL Server那样生成固定精度的精确十进制数值。 - 这类双精度浮点数本身是二进制近似存储结构:像0.09、999999.99这类两位小数的十进制值,是无法被二进制浮点数完全精确表示的,每个值在存储时都会带上极其微小的固有偏差。
- 累加过程中,这些单值的微小偏差不会完全抵消:你样本里10组正的999999.99、10组负的-999999.99,加上一对0.09和-0.09,正负数的主体值虽然可以完全对冲,但每个值附带的浮点存储偏差累加后,就会凑出
-1.31316482687716e-09这种1e-9级别的极小残差。 - 这个偏差不是你的文本解析逻辑、过滤条件写错导致的,也不是数据本身有问题。SQL Server在执行同逻辑查询时,会使用定点十进制规则做精确运算,不存在浮点近似误差,所以能输出精确的0结果。
可落地的解决方法
- 如果需要得到无偏差的精确求和结果,不要依赖SQLite的浮点转换逻辑,可以在SQL层做整数缩放运算绕开浮点误差:把替换逗号后的数值放大100倍转成整数累加,最后再把结果缩回原数量级,示例写法是:
SELECT SUM(CAST(REPLACE(FE1, ',', '.') * 100 AS INTEGER)) / 100.0 FROM DRC WHERE DTS >= '2021-04-21T17:04:44+02:00' AND DTS < '2021-04-21T17:20:09+02:00' AND (DTS IN (SELECT DTS FROM NYN WHERE CNC = '0') OR DTS IN (SELECT DTS FROM ESN WHERE CNC = '0') OR DTS IN (SELECT DTS FROM SZN WHERE CNC = '0') OR DTS IN (SELECT DTS FROM VBN WHERE CNC = '0') )
- 也可以把文本值的解析、累加逻辑放到应用层完成,使用应用侧的精确十进制类型做计算,从根源上避免浮点近似的干扰。
内容的提问来源于stack exchange,提问作者slt
相关产品推荐
相关产品推荐

