PySpark保留两位小数时四舍五入异常问题求助
解决Databricks中round函数四舍五入异常问题
问题原因
你遇到的216.495被舍入为216.49而非216.50,核心原因有两个:
- 浮点数精度丢失:多数十进制小数无法被二进制浮点数精确存储,216.495实际存储的可能是略小于它的近似值(如216.4949999999999...),导致
round函数判定为需要舍去。 - Spark round函数的默认规则:Spark原生
round采用银行家舍入法(四舍六入五成双),当第三位小数为5且前一位为偶数时会舍去,但此场景下更可能是浮点数精度问题主导。
解决方案
方案1:用Decimal类型保证精确计算
将参与计算的列转换为Decimal类型(精确十进制类型),从根源避免浮点数精度丢失:
from pyspark.sql.types import DecimalType from pyspark.sql.functions import col, coalesce, when, round result = ( round( coalesce( when(col('col').cast(DecimalType(18,3)) != 0, col('col').cast(DecimalType(18,3))), when(col('col').cast(DecimalType(18,3)) != 0, col('col').cast(DecimalType(18,3))), when(col('col').cast(DecimalType(18,3)) != 0, col('col').cast(DecimalType(18,3))), when(col('col').cast(DecimalType(18,3)) != 0, col('col').cast(DecimalType(18,3))) ) * col('col4').cast(DecimalType(18,3)) + when((col('col').cast(DecimalType(18,3)) > 0) & (col('col').cast(DecimalType(18,3)) > 0), col('col').cast(DecimalType(18,3)) * col('col').cast(DecimalType(18,3)) ).otherwise(col('col').cast(DecimalType(18,3))), 2 ) .alias('col') )
也可以在读取数据时直接指定列的Decimal类型,减少后续转换操作。
方案2:用format_number实现传统四舍五入
format_number函数遵循传统四舍五入规则格式化数值,转换为数值类型后即可使用:
from pyspark.sql.functions import format_number, col, coalesce, when from pyspark.sql.types import DecimalType result = ( format_number( coalesce( when(col('col') != 0, col('col')), when(col('col') != 0, col('col')), when(col('col') != 0, col('col')), when(col('col') != 0, col('col')) ) * col('col4') + when((col('col') > 0) & (col('col') > 0), col('col') * col('col')).otherwise(col('col')), 2 ) .cast(DecimalType(18,2)) .alias('col') )
方案3:自定义传统四舍五入UDF
如果需要更灵活的逻辑,可自定义UDF实现传统四舍五入:
from pyspark.sql.functions import udf from pyspark.sql.types import DoubleType from pyspark.sql.functions import col, coalesce, when def traditional_round(num, decimals): return round(num + 10**(-decimals-1), decimals) traditional_round_udf = udf(traditional_round, DoubleType()) result = ( traditional_round_udf( coalesce( when(col('col') != 0, col('col')), when(col('col') != 0, col('col')), when(col('col') != 0, col('col')), when(col('col') != 0, col('col')) ) * col('col4') + when((col('col') > 0) & (col('col') > 0), col('col') * col('col')).otherwise(col('col')), 2 ) .alias('col') )
注意事项
- 涉及金融、精确小数计算的场景,优先使用Decimal类型,彻底规避浮点数精度问题。
format_number返回的是字符串类型,需转换为数值类型才能进行后续计算。
内容的提问来源于stack exchange,提问作者Ashwini
相关产品推荐
相关产品推荐

