Skip to content

Fix round_prefer_ceil nearest mode for negative halfway values in Resize op#28345

Merged
titaiwangms merged 4 commits into
mainfrom
copilot/fix-cpu-resize-nearest-differences
May 5, 2026
Merged

Fix round_prefer_ceil nearest mode for negative halfway values in Resize op#28345
titaiwangms merged 4 commits into
mainfrom
copilot/fix-cpu-resize-nearest-differences

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 4, 2026

Description

ROUND_PREFER_CEIL in the Resize operator used bare std::round/roundf, which rounds away from zero. This is correct for positive halfway values (e.g., round(0.5) = 1 = ceil(0.5)) but wrong for negative halfway values (e.g., round(-0.5) = -1, but ceil(-0.5) = 0).

Negative coordinates occur naturally with the half_pixel coordinate transformation mode for the first output pixels when upsampling.

Added an explicit negative-halfway check, mirroring the existing positive-halfway check in ROUND_PREFER_FLOOR:

// CPU (upsamplebase.h)
case ROUND_PREFER_CEIL:
  return [](float x_original, bool) {
    if (x_original == static_cast<int64_t>(x_original) - 0.5f) {
      return static_cast<int64_t>(std::ceil(x_original));
    }
    return static_cast<int64_t>(std::round(x_original));
  };

Same fix applied to the CUDA implementation (resize_impl.cu).

Added two test cases in resize_op_test.cc:

  1. ResizeOpNearestUpSample_RoundPreferCeil_HalfPixel — exercises non-integer scale (26→64) from the original issue report, verifying correct source pixel selection at fractional boundaries.
  2. ResizeOpNearestUpSample_RoundPreferCeil_HalfPixel_2x2to7x8 — exercises a positive 0.5 boundary where round_prefer_ceil selects ceiling.

Motivation and Context

The round_prefer_floor path already had an explicit halfway-case override (for positive values where std::round disagrees with floor). The round_prefer_ceil path was missing the symmetric fix for negative values, violating the ONNX spec semantics of "at ties, prefer ceiling."

…ize op

The ROUND_PREFER_CEIL nearest mode used std::round which rounds away
from zero. For positive halfway values (e.g., 0.5, 1.5), this correctly
rounds to ceiling. However, for negative halfway values (e.g., -0.5,
-1.5), rounding away from zero gives floor instead of ceiling.

Add an explicit check for negative halfway values, paralleling the
existing fix in ROUND_PREFER_FLOOR for positive halfway values. This
ensures the ONNX spec semantics are correctly implemented: at exactly
N.5, prefer ceiling regardless of sign.

Fixed in both CPU (upsamplebase.h) and CUDA (resize_impl.cu)
implementations.

Agent-Logs-Url: https://github.com/microsoft/onnxruntime/sessions/3c71cd3c-5e0f-41ee-8c78-c727c500c5b1

Co-authored-by: tianleiwu <30328909+tianleiwu@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix CPU Resize nearest pixel selection discrepancy with PyTorch Fix round_prefer_ceil nearest mode for negative halfway values in Resize op May 4, 2026
Copilot AI requested a review from tianleiwu May 4, 2026 15:23
@tianleiwu
Copy link
Copy Markdown
Contributor

@copilot, please add a test case for this.

…formation

Add two tests:
1. ResizeOpNearestUpSample_RoundPreferCeil_HalfPixel: exercises non-integer
   scale (26->64) which produces many fractional boundaries
2. ResizeOpNearestUpSample_RoundPreferCeil_HalfPixel_2x2to7x8: exercises
   a positive .5 boundary where round_prefer_ceil selects ceiling

Agent-Logs-Url: https://github.com/microsoft/onnxruntime/sessions/b0f24e47-531c-431c-9dca-505a646f71b7

Co-authored-by: tianleiwu <30328909+tianleiwu@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 4, 2026

@copilot, please add a test case for this.

Added two test cases in commit 076bea0:

  1. ResizeOpNearestUpSample_RoundPreferCeil_HalfPixel — exercises non-integer scale (26→64) from the original issue
  2. ResizeOpNearestUpSample_RoundPreferCeil_HalfPixel_2x2to7x8 — exercises a positive 0.5 boundary where round_prefer_ceil selects ceiling

Both pass on the CPU provider.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can commit the suggested changes from lintrunner.

Comment thread onnxruntime/test/providers/cpu/tensor/resize_op_test.cc Outdated
Copy link
Copy Markdown
Contributor

@tianleiwu tianleiwu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Clean, well-scoped bug fix that adds the missing negative-halfway-value handling to ROUND_PREFER_CEIL in both CPU and CUDA Resize implementations. The fix mirrors the existing ROUND_PREFER_FLOOR positive-halfway check symmetrically. Two new test cases exercise the fix with different scale factors. Low risk.

CPU & CUDA Fix

The fix correctly mirrors the ROUND_PREFER_FLOOR pattern: FLOOR checks + 0.5f for positive halfway values where std::round/roundf (rounds away from zero) disagrees with floor; CEIL now checks - 0.5f for negative halfway values where std::round/roundf disagrees with ceil. The symmetry is clean and easy to reason about.

CUDA implementation uses _Ceil consistently with the existing _Floor usage in NearestPixel_ROUND_PREFER_FLOOR, and static_cast<int> matching the CUDA convention.

Tests

Two complementary test cases: one exercises a non-integer scale (26→64) with negative intermediate coordinates where the bug manifests, the other exercises positive 0.5 boundaries confirming round_prefer_ceil selects ceiling there too. Both follow existing patterns.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@tianleiwu tianleiwu marked this pull request as ready for review May 5, 2026 05:57
@tianleiwu tianleiwu requested a review from titaiwangms May 5, 2026 06:01
@titaiwangms titaiwangms merged commit 80a2352 into main May 5, 2026
89 checks passed
@titaiwangms titaiwangms deleted the copilot/fix-cpu-resize-nearest-differences branch May 5, 2026 20:03
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.

CPU Resize nearest selects different source pixels than PyTorch for 26 to 64 resize

3 participants