pytorch - ✅(Solved) Fix [Bug] Incorrect comparison result in torch.le and torch.ge with uint8 and negative int8 [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
pytorch/pytorch#178716Fetched 2026-04-08 01:48:31
View on GitHub
Comments
1
Participants
2
Timeline
20
Reactions
0
Author
Participants
Timeline (top)
labeled ×7mentioned ×3subscribed ×3cross-referenced ×2

When using torch.le (and similarly torch.ge) to compare a uint8 tensor with a negative int8 scalar, the result is incorrect and differs from NumPy's behavior. This appears to be caused by improper dtype casting (likely casting the negative scalar to uint8), leading to incorrect comparison results.

Root Cause

Description

When using torch.le (and similarly torch.ge) to compare a uint8 tensor with a negative int8 scalar, the result is incorrect and differs from NumPy's behavior. This appears to be caused by improper dtype casting (likely casting the negative scalar to uint8), leading to incorrect comparison results.

Fix Action

Fixed

PR fix notes

PR #178723: Fix incorrect comparison results for uint8 tensor vs signed integer scalar

Description (problem / solution / changelog)

Fixes #178716

Problem

Comparing a uint8 tensor against a negative integer scalar (e.g. torch.le(uint8_tensor, -19)) produces wrong results:

t = torch.tensor([1, 2], dtype=torch.uint8)
torch.le(t, -19)   # tensor([True, True])  ← wrong
torch.ge(t, -19)   # tensor([False, False]) ← wrong

Root cause: combine_categories() in TypeProperties.cpp preserves the tensor's dtype (uint8) when both operands are integral. This causes the int8 scalar -19 to be silently wrapped to uint8(237), so the comparison is 1 <= 237 instead of 1 <= -19.

Fix

After compute_common_dtype() runs in TensorIteratorBase::compute_types(), check whether any wrapped-number scalar operand has a different integral dtype than the computed common_dtype_. If so, re-promote using c10::promoteTypes(), which correctly yields int16 for (uint8, int8), preserving the sign of -19.

The check is gated on a new is_comparison_op_ flag (set inside set_up_comparison_op_config()) so arithmetic ops are completely unaffected. It is type-only — no .item() call, no CUDA synchronisation, zero runtime overhead.

t = torch.tensor([1, 2], dtype=torch.uint8)
torch.le(t, -19)   # tensor([False, False]) ✓
torch.ge(t, -19)   # tensor([True, True])   ✓

Changed files

FileChange
aten/src/ATen/TensorIterator.hAdd is_comparison_op_ bool field + setter to TensorIteratorConfig
aten/src/ATen/TensorIterator.cppRe-promote common_dtype_ when a wrapped scalar's integral type differs; set the flag in set_up_comparison_op_config()
test/test_type_promotion.pyRegression test covering negative scalar, out-of-bounds positive scalar, and Python literal cases

cc @ezyang @albanD @bdhirsh @gchanan @zou3519

Changed files

  • aten/src/ATen/TensorIterator.cpp (modified, +22/-0)
  • aten/src/ATen/TensorIterator.h (modified, +6/-0)
  • test/test_type_promotion.py (modified, +21/-0)

Code Example

# torch.le
import numpy as np
import torch
input = torch.tensor(np.array([1, 2, 3, 4]), dtype=torch.uint8)
other = torch.tensor(-19, dtype=torch.int8)
out = torch.le(input,other)
print(out) # out: tensor([True, True, True, True])

input = np.array([1, 2, 3, 4], dtype=np.uint8)
other = np.int8(-19)
out = np.less_equal(input, other)
print(out) # out: [False False False False]

# torch.ge
input = torch.tensor(-19, dtype=torch.int8)
other = torch.tensor(np.array([1, 2, 3, 4]), dtype=torch.uint8)
out = torch.ge(input,other)
print(out) # out: tensor([True, True, True, True])

input = np.int8(-19)
other = np.array([1, 2, 3, 4], dtype=np.uint8)
out = np.greater_equal(input, other)
print(out) # out: [False False False False]
RAW_BUFFERClick to expand / collapse

🐛 Describe the bug

Description

When using torch.le (and similarly torch.ge) to compare a uint8 tensor with a negative int8 scalar, the result is incorrect and differs from NumPy's behavior. This appears to be caused by improper dtype casting (likely casting the negative scalar to uint8), leading to incorrect comparison results.

Reproducible Example

# torch.le
import numpy as np
import torch
input = torch.tensor(np.array([1, 2, 3, 4]), dtype=torch.uint8)
other = torch.tensor(-19, dtype=torch.int8)
out = torch.le(input,other)
print(out) # out: tensor([True, True, True, True])

input = np.array([1, 2, 3, 4], dtype=np.uint8)
other = np.int8(-19)
out = np.less_equal(input, other)
print(out) # out: [False False False False]

# torch.ge
input = torch.tensor(-19, dtype=torch.int8)
other = torch.tensor(np.array([1, 2, 3, 4]), dtype=torch.uint8)
out = torch.ge(input,other)
print(out) # out: tensor([True, True, True, True])

input = np.int8(-19)
other = np.array([1, 2, 3, 4], dtype=np.uint8)
out = np.greater_equal(input, other)
print(out) # out: [False False False False]

Expected Behavior

A safer promotion rule (like NumPy) would cast both operands to a signed integer type (e.g., int16 or int32) to preserve correctness.

Versions

PyTorch version: 2.11.0+cu130 Is debug build: False CUDA used to build PyTorch: 13.0 ROCM used to build PyTorch: N/A

OS: Ubuntu 20.04.6 LTS (x86_64) GCC version: (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0 Clang version: Could not collect CMake version: Could not collect Libc version: glibc-2.31

Python version: 3.10.20 (main, Mar 11 2026, 17:46:40) [GCC 14.3.0] (64-bit runtime) Python platform: Linux-5.15.0-67-generic-x86_64-with-glibc2.31

cc @mruberry @rgommers @nairbv

extent analysis

Fix Plan

To fix the issue, we need to ensure that both operands are cast to a signed integer type before comparison. We can achieve this by explicitly casting the operands to a signed type.

  • Cast the uint8 tensor to int8 or a larger signed integer type (e.g., int16 or int32) before comparison.
  • Cast the int8 scalar to a larger signed integer type (e.g., int16 or int32) before comparison.

Example code:

import torch
import numpy as np

# torch.le
input_tensor = torch.tensor(np.array([1, 2, 3, 4]), dtype=torch.uint8)
other_scalar = torch.tensor(-19, dtype=torch.int8)

# Cast input_tensor to int16
input_tensor_casted = input_tensor.to(torch.int16)
other_scalar_casted = other_scalar.to(torch.int16)

out = torch.le(input_tensor_casted, other_scalar_casted)
print(out)  # out: tensor([False, False, False, False])

# torch.ge
input_scalar = torch.tensor(-19, dtype=torch.int8)
other_tensor = torch.tensor(np.array([1, 2, 3, 4]), dtype=torch.uint8)

# Cast other_tensor to int16
other_tensor_casted = other_tensor.to(torch.int16)
input_scalar_casted = input_scalar.to(torch.int16)

out = torch.ge(input_scalar_casted, other_tensor_casted)
print(out)  # out: tensor([False, False, False, False])

Verification

To verify that the fix worked, compare the results of the modified code with the expected behavior. The output should match the NumPy results.

Extra Tips

  • Be cautious when comparing tensors with different data types, as implicit casting can lead to unexpected results.
  • Consider using explicit casting to ensure that both operands have the same data type before comparison.
  • If possible, use a consistent data type throughout your code to avoid type-related issues.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING