You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spark exceptAll对比float与double列出现异常差值的原因咨询

Spark float/double类型exceptAll结果异常的原理说明

偏差的产生起点:浮点数的固有存储特性

所有遵循IEEE 754标准的float(32位单精度)、double(64位双精度)类型,都采用二进制科学计数法存储数值,绝大多数十进制小数无法用有限位二进制精确表示,只能存储为对应精度下最接近目标值的近似值:

  • 代码中84.99F、9.99F作为float类型字面量,赋值完成时存储的就不是精确的84.99、9.99,只是单精度精度范围内最接近两个十进制数的二进制近似值。
  • 代码中84.99、9.99作为double类型字面量,存储的是双精度精度范围内最接近两个十进制数的二进制近似值,由于双精度可用位数更多,这个近似值和真实十进制值的误差远小于float类型的误差。

为什么show()输出看不出差异

DataFrame的show()方法默认对浮点数做截断显示,不会输出完整精度的底层存储值,因此float和double存储的两个误差不同的近似值,截断后都会显示为84.99、9.99,肉眼无法直接识别到底层值的差异。

exceptAll执行环节的处理逻辑

调用exceptAll时,Spark首先会对齐两侧DataFrame的Schema,具体处理逻辑如下:

  1. 识别到左侧dec列是float类型、右侧dec列是double类型,会自动将左侧float值隐式转换为double类型后再做等值比较,这个隐式转换和你手动调用cast("double")的逻辑完全一致。
  2. float转double的过程不会补全新的精度,只是把原来float存储的近似值原封不动拓展为64位的double表示:例如float类型存储的84.99近似值本质是84.98999786376953,转成double后值不会发生任何变化,不会自动修正为double精度下更接近84.99的近似值。
  3. 右侧l2中double类型的84.99存储的是双精度下的近似值(约等于84.990000000000002),和左侧float转换得到的84.98999786376953值不相等,因此会被判定为不存在于右侧集合,最终作为exceptAll的结果输出,也就是你看到的带长小数尾巴的值。

你可以直接在Scala REPL中运行以下代码验证底层真值:

// 打印float字面量转double后的真实值
println(84.99F.doubleValue()) // 输出 84.98999786376953
println(9.99F.doubleValue())  // 输出 9.989999771118164

注意:这不是Spark的功能bug,是浮点数存储规则带来的通用问题。任何需要精确等值比较、涉及金额等精确小数的场景,不要使用float/double类型,应该选用Decimal类型存储,避免精度误差带来的逻辑错误。

内容的提问来源于stack exchange,提问作者Ashu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 16:09:09