Converting F images to I turns large values, inf and NaN into -2147483648

Open Beginner friendly
#10,372 4 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@usmanmateen is already working on this.

Since Oct 31, 2026.

  • #10373 by @usmanmateen — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
72/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
c, python
Domain
backend

Research direction

The bug is the unchecked cast i = f; in the f2i function in src/libImaging/Convert.c; clip32() in src/libImaging/Filter.c shows the existing range guard to follow. Reproduce with the Python snippet in the issue, then confirm the F to I results. Done when values above the INT32 range clip to 2147483647, -inf clips to -2147483648, NaN becomes 0, and the round trip returns 2147483647.

Written by the indexing model from the issue text.

Description

Conversion
What did you do?

Converted a float (F) image with large or non-finite values to I, for example a float TIFF or a NumPy result with inf or NaN pixels, or an I image that went through F for processing:

import numpy as np
from PIL import Image

im = Image.fromarray(np.array([[1.5, 70000, 3e9, np.inf, -np.inf, np.nan]], np.float32), "F")
print("I", np.asarray(im.convert("I")).tolist())
print("L", np.asarray(im.convert("L")).tolist())

print(Image.new("I", (1, 1), 2**31 - 1).convert("F").convert("I").getpixel((0, 0)))
What did you expect to happen?

Values outside the I range clipped to it, the way F to L and F to I;16 clip to theirs, and the round trip to keep 2147483647:

I [[1, 70000, 2147483647, 2147483647, -2147483648, 0]]
L [[1, 255, 255, 255, 0, 0]]
2147483647
What actually happened?

Every value above the INT32 range, inf and NaN become -2147483648, the most negative value:

I [[1, 70000, -2147483648, -2147483648, -2147483648, -2147483648]]
L [[1, 255, 255, 255, 0, 0]]
-2147483648

The round trip fails because 2147483647 isn't representable as a float32 and rounds up to 2147483648.0, which is just past the INT32 range.

f2i in Convert.c casts with i = f; and no range check. Converting an out-of-range float or NaN to an integer is undefined behavior in C (-fsanitize=float-cast-overflow reports it), and the result depends on the CPU: x86 gives INT_MIN, while ARM saturates and gives INT32_MAX for large values and 0 for NaN. clip32() in Filter.c already guards the same overflow to INT_MIN.

I can open a PR that clips to the INT32 range in f2i and maps NaN to 0.

What are your OS, Python and Pillow versions?
  • OS: Ubuntu 26.04 (WSL2), x86-64
  • Python: 3.12.13
  • Pillow: 13.0.0
Dominant language
Python
Stars
14k
Forks
2.6k
Avg merge
1d 4h
Merged PRs (30d)
182

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from python-pillow/Pillow

All issues in python-pillow/Pillow

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.