Li Guo created SPARK-59505:
------------------------------

             Summary: Arrow conversion rounds decimals HALF_EVEN, so Arrow 
Python UDFs and Connect createDataFrame return different values than SQL and 
Classic
                 Key: SPARK-59505
                 URL: https://issues.apache.org/jira/browse/SPARK-59505
             Project: Spark
          Issue Type: Bug
          Components: Connect, PySpark
    Affects Versions: 4.1.3, 4.2.0, 4.3.0, 5.0.0
            Reporter: Li Guo


{{LocalDataToArrowConversion}} rescales a Python {{Decimal}} to the declared 
scale with {{decimal.Context(prec=..., rounding=decimal.ROUND_HALF_EVEN)}} 
(python/pyspark/sql/conversion.py, added by SPARK-53938). The JVM rounds 
HALF_UP whenever it rescales a decimal ({{Decimal.set}}, 
{{Decimal.changePrecision}}, {{CAST(x AS DECIMAL(p, s))}}), and the pickled 
Python UDF and UDTF paths and Classic {{createDataFrame}} inherit that because 
the JVM does the rescale for them. So the Arrow-optimized Python UDF (the 
default since 4.2), Arrow UDTFs, Python data sources and Spark Connect's 
{{createDataFrame}} produce a different number than SQL for any value that sits 
on a rounding tie.

{code:python}
from decimal import Decimal
from pyspark.sql.functions import col, udf
from pyspark.sql.types import DecimalType

df = spark.sql("SELECT * FROM VALUES ('1.005'), ('1.025'), ('0.125') AS t(v)")
[r[0] for r in df.select(col("v").cast(DecimalType(20, 2))).collect()]
# [Decimal('1.01'), Decimal('1.03'), Decimal('0.13')]

f = udf(lambda v: Decimal(v), DecimalType(20, 2), useArrow=True)   # the 
default since 4.2
[r[0] for r in df.select(f("v")).collect()]
# [Decimal('1.00'), Decimal('1.02'), Decimal('0.12')]

f2 = udf(lambda v: Decimal(v), DecimalType(20, 2), useArrow=False)
[r[0] for r in df.select(f2("v")).collect()]
# [Decimal('1.01'), Decimal('1.03'), Decimal('0.13')]
{code}

Spark Connect and Classic also disagree on the same input: 
{{spark.createDataFrame([(Decimal("1.005"),)], "d decimal(20, 2)")}} gives 1.01 
in Classic and 1.00 through a Connect session. Reproduced on released 4.2.0 and 
on master, in Classic and Connect sessions.

Fix: use {{ROUND_HALF_UP}} in that context. One line plus tests on the 
converter, the Arrow UDF, the Arrow UDTF, a Python data source and 
createDataFrame in both modes. branch-4.1, branch-4.2 and branch-4.3 carry the 
same line, so the fix backports cleanly.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to