Skip to content

MAINT: Improve float16 and float32 printing #28703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 18, 2025
Merged

MAINT: Improve float16 and float32 printing #28703

merged 8 commits into from
Apr 18, 2025

Conversation

f2013519
Copy link
Contributor

@f2013519 f2013519 commented Apr 14, 2025

This patch fixes #28679.

Summary of changes: Update the max exponent cutoff for float16 and float32 (and corresponding complex) types to avoid weirdness when float types can no longer exactly represent integers.

Before the change:
print(np.half(65504)) # 65500

After the change:
print(np.half(65504)) # 6.55e+04

For a detailed background on the motivation, please check the linked issue.

@f2013519
Copy link
Contributor Author

@mhvk @tylerjereddy, PTAL, thanks!

Copy link
Contributor

@mhvk mhvk left a comment

Choose a reason for hiding this comment

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

Thanks! It looks good, though I have some minor comments. Also a suggestion: let's include a comment in both places cross-referencing each other, so that if we ever change one, we will not forget to change the other too.

@@ -919,6 +919,21 @@ def test_floatmode(self):
a = np.float64.fromhex('-1p-97')
assert_equal(np.float64(np.array2string(a, floatmode='unique')), a)

def test_gh_28679(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

Here and below, I'd tend to pytest.mark.parametrize("value, expected", [np.half(...), "[999. 999.]"), ..., but no big deal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was following the style used in other places in the file. Will keep this as is for now ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As others have requested to update this to parametrize, I will work on that.

Copy link
Contributor

@tylerjereddy tylerjereddy left a comment

Choose a reason for hiding this comment

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

I added one stress testing-related comment where I'm not fully convinced just yet. I'm sure Marten knows more than I about the rest of the analysis and his comments look sensible to me in the other places (+1 for parametrization I think).

I'm going to go ahead and close gh-28696--I think it has served its purpose to refresh the discussion and refocus this to a solution the team seems to like better (and I think I agree, modulo my query about consistency).

Copy link
Contributor

@mhvk mhvk left a comment

Choose a reason for hiding this comment

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

I think this looks good, so do my remaining comment only if there is a need to edit anything else.

I guess one larger question is whether there should be a what's-new fragment. Since this might affect downstream doctests, perhaps that is a good idea. What do others think? (it would be doc/release/upcoming_changes/28703.change.rst, I think; see README.rst in that directory).

@@ -990,11 +990,15 @@ def fillFormat(self, data):

# choose exponential mode based on the non-zero finite values:
abs_non_zero = absolute(finite_vals[finite_vals != 0])

# consider data type while deciding the max cutoff for exp format
Copy link
Contributor

Choose a reason for hiding this comment

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

If there is another round, then might as put this in the if statement. Indeed, in principle one can do the assignment with the walrus,

if len(abs_non_zero := absolute(finite_vals[finite_vals != 0])) != 0:
    exp_cutoff_max = ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@ngoldbaum
Copy link
Member

Do we need a new revision of the legacy array printing format?

Definitely needs a release note, also needs a ping to the mailing list probably, just to alert of possible downstream doctesting noise with a note to configure doc builds to use the legacy print format in doctests.

@ngoldbaum ngoldbaum added the 56 - Needs Release Note. Needs an entry in doc/release/upcoming_changes label Apr 15, 2025
@mhvk
Copy link
Contributor

mhvk commented Apr 15, 2025

Yes, you are right, we definitely should allow people to get the old format back by setting legacy -- should really have thought of that myself!

@f2013519
Copy link
Contributor Author

So we want to fallback to old behavior in case of:

numpy.set_printoptions(legacy="2.2")

correct?

@ngoldbaum
Copy link
Member

2.2 or older, yeah

@tylerjereddy
Copy link
Contributor

I confirmed that the property-based test passes for 16- and 32-bit types here now.

As an aside, Marten and I both asked for parametrized testing and I'd prefer if reviewer comments could be marked as resolved by the reviewers since that change was declined by author and then marked resolved. Not a big deal, but also not really a great reason for push back on it either, and certainly would prefer if the maintainer resolved it.

@f2013519
Copy link
Contributor Author

I confirmed that the property-based test passes for 16- and 32-bit types here now.

As an aside, Marten and I both asked for parametrized testing and I'd prefer if reviewer comments could be marked as resolved by the reviewers since that change was declined by author and then marked resolved. Not a big deal, but also not really a great reason for push back on it either, and certainly would prefer if the maintainer resolved it.

I've unmarked it. Since people feel strongly I'll update the tests to use parameterized testing (maybe tomorrow). Pushing another incremental commit to add legacy support.

@ngoldbaum ngoldbaum removed the 56 - Needs Release Note. Needs an entry in doc/release/upcoming_changes label Apr 15, 2025
@f2013519
Copy link
Contributor Author

f2013519 commented Apr 16, 2025

I believe I have resolved all outstanding review comments, PTAL.

Also, while looking into legacy support I found get_printoptions() is broken for legacy mode 2.1 on latest. I've fixed that and added a sanity test for all supported legacy print modes with get_printoptions.

@f2013519
Copy link
Contributor Author

Is there anything else that needs to be looked into before we merge this @ngoldbaum ?

@ngoldbaum ngoldbaum added this to the 2.3.0 release milestone Apr 18, 2025
@ngoldbaum
Copy link
Member

I'll wait for the other reviewers to give this a look.

Keep in mind that this won't be available to new users until NumPy 2.3, so there's no particular rush to get this merged before the first RC goes out.

I added the 2.3 milestone so this won't get dropped.

Copy link
Contributor

@mhvk mhvk left a comment

Choose a reason for hiding this comment

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

This all looks great now, let's get it in! Thanks, @f2013519 - this is a small but nice change that will avoid confusion in the future!

@mhvk mhvk merged commit 9843e15 into numpy:main Apr 18, 2025
72 checks passed
@f2013519
Copy link
Contributor Author

This all looks great now, let's get it in! Thanks, @f2013519 - this is a small but nice change that will avoid confusion in the future!

Thanks to everyone for your feedback/suggestions!

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

Successfully merging this pull request may close these issues.

BUG: Floating point printing / string conversion is incorrect
4 participants