Skip to content

[SPARK-59093][PS] Fix incorrect results and arithmetic errors in floor division - #58397

Open
Spenserrrr wants to merge 2 commits into
apache:masterfrom
Spenserrrr:numpy-floordiv-dunder
Open

[SPARK-59093][PS] Fix incorrect results and arithmetic errors in floor division#58397
Spenserrrr wants to merge 2 commits into
apache:masterfrom
Spenserrrr:numpy-floordiv-dunder

Conversation

@Spenserrrr

@Spenserrrr Spenserrrr commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

IntegralOps and FractionalOps now compute // and its reflected form with _floor_divide_func, the expression added in SPARK-58581. The first commit moves that helper from numpy_compat.py to pyspark.pandas.utils so the core operator does not import the NumPy compatibility layer.

Why are the changes needed?

F.floor(a / b) rounds before flooring, casts integral operands to double, and returns a bigint, so // gives wrong values (ps.Series([1.0]) // 0.1 returns 10.0, pandas 9.0) and raises ARITHMETIC_OVERFLOW when the quotient is infinite. SPARK-58581 fixed this arithmetic in the np.floor_divide mapping, but that entry is unreachable, so the operator users actually call was left unchanged.

Does this PR introduce any user-facing change?

Yes. // matches pandas where it previously did not: ps.Series([1.0, 10.0]) // 0.1 returns [9.0, 99.0] instead of [10.0, 100.0]. A quotient above 2**53 is still rounded, since the result type has to stay double.

How was this patch tested?

Rows added to the existing test_floordiv and test_rfloordiv, each confirmed to fail without the change, plus the existing pandas-on-Spark suites with ANSI mode on and off.

Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Claude Opus 5)

`_floor_divide_floating`, `_floor_divide_integral` and `_floor_divide_func` were
added to `numpy_compat.py` for the `np.floor_divide` mapping, but the `//` operator
in `data_type_ops/num_ops.py` needs the same expression. `num_ops` is the core
operator implementation and `numpy_compat` is the NumPy compatibility layer on top
of it, so importing the helpers from there would invert the layering.

`pyspark.pandas.utils` already hosts shared column expressions such as the
`compare_null_first` family, and it imports no pandas-on-Spark module other than
`_typing` and `typedef.typehints`, so both callers can reach it without a cycle.

This is a pure move: the functions are unchanged, `numpy_compat` keeps its mapping
entry and imports the helper, and the existing test imports it from its new home.
…r division

`IntegralOps` and `FractionalOps` computed `//` as `F.floor(a / b)` for both the
forward and the reflected operator. That is wrong in three ways and fails outright
in a fourth:

- the division rounds before the floor, so `ps.Series([1.0]) // 0.1` returned 10.0
  where pandas returns 9.0;
- integral operands are cast to double first, so `-(2**53 + 1) // 2` returned
  -4503599627370496.0 instead of -4503599627370497;
- an infinite divisor returned 0.0 for every dividend, where pandas derives the
  quotient from the remainder and returns -1.0 for opposite signs;
- `F.floor` returns a bigint, so an infinite or out-of-range quotient raised
  ARITHMETIC_OVERFLOW under ANSI mode and saturated to Long.MAX_VALUE without it,
  for example `ps.Series([np.inf]) // 2.0` and `1e300 // 1e-300`. The reflected
  operator additionally raised DIVIDE_BY_ZERO for `0 // ps.Series([0])`, because
  its zero-divisor branch divided an infinity by the dividend.

SPARK-58581 fixed the same arithmetic in the `np.floor_divide` mapping, which is
unreachable: `np.floor_divide` is dispatched to this dunder operation before the
mapping registry is consulted. The four methods now reuse that expression, so both
paths agree and the zero-divisor answers come from literals rather than a division,
which makes the result the same under ANSI mode and without it.

A quotient above 2**53 is still rounded, since the result type has to stay double
for the infinities a zero divisor produces.
@Spenserrrr
Spenserrrr marked this pull request as ready for review August 28, 2026 23:24
@Spenserrrr

Spenserrrr commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

Hi @zhengruifeng! This is the follow-up I mentioned in #58306. That PR fixed _floor_divide_func, but the mapping entry is unreachable. Thus in this PR, I revise the four num_ops floordiv/rfloordiv so that it uses the expressions in that PR and I also move these expressions into the utility file. Could you take a look when you have time? Thanks!
cc @Yicong-Huang

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant