Skip to content

py/obj: Fix nan handling in REPR_C and REPR_D. #17531

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 1 commit into from
Jun 23, 2025

Conversation

yoctopuce
Copy link
Contributor

@yoctopuce yoctopuce commented Jun 19, 2025

Summary

CPython math.nan is positive with regards to math.copysign().
In MicroPython, this is only true with REPR_A and REPR_B.

In addition, REPR_C and REPR_D (nanbox) should only use the true nan to prevent system crash in case of hand-crafted floats.

This problem has been discovered while working on a test code for #17444
Fixing the issue without changing the constant used for math.nan did break an existing copysign test case.

Testing

A new test case has been added to specific test the behaviour of math constants with regard to copysign.
Test cases creating special nans that were improperly interpreted in REPR_C have also been added.

Trade-offs and Alternatives

Fixing nan signaling flag does not change code size.

The extra check in mp_obj_new_float will increase code size, but

  1. nanbox platform is probably not very sensitive to larger code as it uses 64 bit objects. As the added code prevents a crash, it is probably worth the small size increase.
  2. The impact on REPR_C appears to be quite small (as tested on ARMv4), and is actually more than compensated by removing the unnecessary bit-clearing code in the opposite function (mp_obj_float_get). Indeed, leaving bit 1 provides a better estimation of the original float value than always biasing the result toward zero by clearing both low bits.

Copy link

codecov bot commented Jun 19, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.57%. Comparing base (66c0148) to head (e57aa7e).
Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #17531   +/-   ##
=======================================
  Coverage   98.57%   98.57%           
=======================================
  Files         169      169           
  Lines       21968    21968           
=======================================
  Hits        21654    21654           
  Misses        314      314           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
     mimxrt:    +0 +0.000% TEENSY40
        rp2:    +0 +0.000% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@yoctopuce
Copy link
Contributor Author

I ended up adding the REPR_C fix in the same PR as the impact on code footprint does not seems to be a problem on my ARMv4 platform, even when keeping the function inline.

@dpgeorge dpgeorge added the py-core Relates to py/ directory in source label Jun 20, 2025
Copy link
Member

@dpgeorge dpgeorge left a comment

Choose a reason for hiding this comment

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

Thanks for splitting this out into a separate PR.

For esp8266 it saves 16 bytes of code size!

But, 20 tests now fail on esp8266 😞

Here are the failing tests:

20 tests failed: basics/nanbox_smallint.py float/builtin_float_abs.py float/builtin_float_pow.py float/builtin_float_round.py float/bytearray_construct_endian.py float/bytes_construct_endian.py float/float1.py float/float2int_fp30_intbig.py float/float_divmod_relaxed.py float/float_parse.py float/float_struct.py float/float_struct_e.py float/inf_nan_arith.py float/math_domain.py float/math_domain_python311.py float/math_fun.py float/math_fun_bool.py float/math_fun_int.py float/string_format_fp30.py float/string_format_modulo.py

Eg the output of float/builtin_float_abs.py is:

1.0 1.0
-1.0 1.0
0.0 2e-45
-0.0 2e-45
nan nan
-nan nan
inf nan
-inf nan

Lines 3 and 4 are wrong, they should be 0.0, but because bit 1 is not being cleared they are now very small numbers.

Lines 7 and 8 are wrong, they should be inf, but because bit 1 is not being cleared they are now nan.

So I don't think it's a good idea to drop the & ~3u. I can understand the motivation (to round up some numbers), and it actually makes tests/misc/rge_sm.py nearly pass now! That would be great, but I don't think this is the right approach.

(I did actually try something similar myself but instead in mp_obj_new_float() I tried rounding up the 30-bit float by adding 0x04 if bit 1 was set in the original float. That gave quite good results, but I didn't get a chance to tidy that up for a PR yet.)

@yoctopuce yoctopuce force-pushed the fix_math_nan branch 2 times, most recently from 84b1918 to 5cb1af1 Compare June 20, 2025 06:00
@yoctopuce
Copy link
Contributor Author

@dpgeorge Sorry for the first commit yesterday that failed so many tests. I did not immediately figure out how I could get a 32bit Linux build on Ubuntu 24.04 for easily running the full CI tests.

I have now pushed a new version that fixes it, and takes your comments into account.
Can you tell how much bigger the esp8266 binary gets ?
I tried to build it myself, but was unable to find a place to download the proper tools (links in the ReadMe are now 404)

@dpgeorge
Copy link
Member

Can you tell how much bigger the esp8266 binary gets ?

It's now +44 bytes. That seems OK, considering it fixes a bug.

Running the tests with ESP8266_GENERIC firmware, they are now all passing, including the modified float_array.py test (but note that math_constants_extra.py doesn't run on this target -- it skips -- because it has these extra constants disabled).

@yoctopuce
Copy link
Contributor Author

Can you tell how much bigger the esp8266 binary gets ?

It's now +44 bytes. That seems OK, considering it fixes a bug.

Excellent, thank you !

Running the tests with ESP8266_GENERIC firmware, they are now all passing, including the modified float_array.py test (but note that math_constants_extra.py doesn't run on this target -- it skips -- because it has these extra constants disabled).

I have been able to verify that math_constants_extra.py works using a 32bit unix build with REPR_C. Testing this as part of standard CI comes back to the discussion we had with @projectgus about having a REPR_C + longlong variant. I can work on it once his PR will be pulled in.

Copy link
Member

@dpgeorge dpgeorge left a comment

Choose a reason for hiding this comment

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

Thanks for updating, looks good now!

Tested again on esp8266 and the tests pass.

CPython math.nan is positive with regards to copysign.  The signaling bit
(aka sign flag) was incorrectly set.

In addition, REPR_C and REPR_D should only use the _true_ nan to prevent
system crash in case of hand-crafted floats.  For instance, with REPR_C,
any nan-like float following the pattern
`01111111 1xxxxxxx xxxxxxxx xxxxx1xx` would be switched to an immediate
object or a qstr string.  When the qstr index is too large, this would
cause a crash.

This commit fixes the issue, and adds the relevant test cases.

Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
@dpgeorge dpgeorge merged commit e57aa7e into micropython:master Jun 23, 2025
69 checks passed
@yoctopuce yoctopuce deleted the fix_math_nan branch June 23, 2025 14:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
py-core Relates to py/ directory in source
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy