🐛 [Bug] torch.fmod returns a tiny value with the wrong sign when the float quotient rounds up
Maintainers usually reply within 3 days
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 70/100
Research direction
Start in impl.elementwise.fmod and its helper trunc_div in py/torch_tensorrt/dynamo/conversion/impl/elementwise/ops.py, and check how the float quotient is floored before the remainder is computed. Run the reproduction script from the issue on a CUDA machine and compare against torch.fmod for 4.2 / 1.4 and the 0.1 wrapping case. Done means float fmod results match PyTorch in sign and value while integer behaviour is unchanged.
Written by the indexing model from the issue text.
Description
Bug Description
torch.fmod(a, b) on floating point tensors returns a tiny value with the wrong sign when a / b rounds up to a whole number. In float32, torch.fmod(4.2, 1.4) is 1.3999999 in PyTorch and -1.19e-07 in Torch-TensorRT. The result of fmod always has the sign of a, so a negative result for a positive a is never right. It shows up when wrapping values by a period: fmod(x, 0.1) for x = 0.1, 0.2, ..., 10.0 differs from PyTorch for 43 of the 100 values.
impl.elementwise.fmod in py/torch_tensorrt/dynamo/conversion/impl/elementwise/ops.py computes input - trunc_div(input, other) * other, and trunc_div takes FLOOR_DIV of |input| and |other|. For floats, the division is rounded before it's floored. In float32, 4.2 / 1.4 is 2.99999991, which rounds to 3.0, so the quotient comes out as 3 instead of 2 and input - 3 * other is just below zero. PyTorch computes fmod exactly and gets 2.
torch.remainder (%) gives the right result for these inputs.
To Reproduce
import torch
import torch_tensorrt
class Fmod(torch.nn.Module):
def forward(self, a, b):
return torch.fmod(a, b)
def trt(model, *args):
return torch_tensorrt.compile(model.eval().cuda(), ir="dynamo", inputs=list(args), min_block_size=1)(*args)
a = torch.tensor([4.2, -4.2, 0.7, 5.0], device="cuda")
b = torch.tensor([1.4, 1.4, 0.1, 3.0], device="cuda")
print(Fmod()(a, b).tolist())
print(trt(Fmod(), a, b).tolist())
# Wrapping multiples of 0.1 by 0.1
x = torch.arange(1, 101, device="cuda") * 0.1
period = torch.full_like(x, 0.1)
expected, got = Fmod()(x, period), trt(Fmod(), x, period)
print((got - expected).abs().gt(1e-3).sum().item(), "of", x.numel(), "differ")
Output:
[1.3999998569488525, -1.3999998569488525, 0.09999997913837433, 2.0]
[-1.1920928955078125e-07, 1.1920928955078125e-07, -2.2351741790771484e-08, 2.0]
43 of 100 differ
Expected behavior
The same results as PyTorch, which always have the sign of a.
Environment
- Torch-TensorRT Version: 2.14.0 (pip).
fmodandtrunc_divare the same on main. - PyTorch Version: 2.14.1+cu130
- CPU Architecture: x86_64
- OS: Ubuntu 26.04 (WSL2)
- How you installed PyTorch: pip
- Python version: 3.12
- CUDA version: 13.0 (TensorRT 11.1.0.106)
- GPU models and configuration: NVIDIA GeForce RTX 5060 Ti, driver 596.49
Additional context
I found this with torchparity, a tool I'm writing that runs PyTorch ops through converters and compares the results with eager PyTorch. I have a fix with tests and can open a PR. When the result has the sign opposite to input, it adds |other| back with the sign of input. Integer inputs are unchanged.
#4786 changes trunc_div for large integers and doesn't cover this.
- Dominant language
- Python
- Stars
- 3k
- Forks
- 421
- Avg merge
- 4d 7h
- Merged PRs (30d)
- 62
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from pytorch/TensorRT
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
Maintainers usually reply within 3 days
-
Removing ptq.hOpenStory: Build & Install & Packaging
Difficulty 1/5 Under an hour Newbie friendliness 65/100
Maintainers usually reply within 3 days
-
Difficulty 4/5 3-5 days Newbie friendliness 22/100
Maintainers usually reply within 3 days
-
Difficulty 4/5 3-5 days Newbie friendliness 18/100
Maintainers usually reply within 3 days
-
Difficulty 3/5 Half a day Newbie friendliness 65/100
Maintainers usually reply within 3 days
All issues in pytorch/TensorRT
Similar issues
-
plot_graph crashes when a mesh node has no m2m edgePossibly taken @tygvi claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
mllam/neural-lam#883 · 1 comment ·
Maintainers usually reply within 2 days
-
enhancement possible-duplicate
Difficulty 1/5 1-3 hours Newbie friendliness 78/100
AOSSIE-Org/PictoPy#1715 · 2 comments ·
Maintainers usually reply within 6 days
-
SQLite connector omits generated columns from the catalog, and drops relationships sourced on themOpen
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
NVIDIA/auto-ontology#511 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
ethereum/execution-specs#3996 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
cpp-best-practices/project_validator#267 ·
Maintainers usually reply within 1 day