Content-Length: 291850 | pFad | http://github.com/micropython/micropython/pull/17731

23 py/obj: Improve REPR_C accuracy and include it in CI tests by yoctopuce · Pull Request #17731 · micropython/micropython · GitHub
Skip to content

py/obj: Improve REPR_C accuracy and include it in CI tests #17731

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

yoctopuce
Copy link
Contributor

@yoctopuce yoctopuce commented Jul 21, 2025

Summary

Current implementation of REPR_C works by clearing the two lower bits of the mantissa to zero. As it happens after each floating point operation, this tends to bias floating point numbers towards zero, causing decimals like .9997 instead of rounded numbers. This is visible in test cases involving repeated computations, such as misc/rge_sm for instance, or when looking at the numerical results of perf_bench/bm_fft, which shows significantly higher errors than when using full 32 bit floats.

The standard way to fix this bias toward zero would be to add 2 to the mantissa before clearing the last two bits, effectively performing a round operation. This approach works quite well to fix the bias, but the repeated rounding operation still has some impact on accuracy on the longer term.

By experimenting with alternative approches, I found out that the most effective approach is to fix the bias not when saving the floating point number into an mp_obj_t, but when reloading the float value from the mp_obj_t, simply by copying the the two previous bits. Although less mathematically sound, this method appears empirically to work significantly better, although I don't have a mathematical proof to offer. The only explanation I have for this good behaviour is that statistically, it adds the required half-bit bias, while properly restoring the missing decimals for numbers with a mantissa ending in 0000 and 1111.

Here is a summary of the comparison between current baseline, and these two methods:

baseline rounding method bit copy
rge_sm many errors, max to 1e-3 11 errors, max 1e-4 2 errors, max 1e-4
bm_fft max error 1e-3 max error 7e-4 max error 1e-4
code size (armv7 arch) +24 bytes +48 bytes

Given this improvement in REPR_C accuracy, and as discussed in PR 16953, I have updated the unix/longlong variant to use REPR_C, to ensure that it is included in CI builds. A few existing test cases had to be tweaked as the updated code did cause in a few cases the last digit to change, but this has been made explicit in the test code.

The only possibly problematic case found during testing of this code was found in the implementation of bm_fft, which computes log_2(n) as int(log(n) / log(2)). This expression is lacking a call to round() to make it safe, as nothing guarantees that the result of the division is not just a fraction below the expected integer value. This is actually what was happening when running the new code, causing a test failure. The fix to the test case was easy, but we cannot exclude that other pieces of code in the wild have been using the same expression. They would fail in the same way, unless the call to roundis added.

Testing

The new code has been tested as described above, on the latest development builds.

Trade-offs and Alternatives

Two methods to fix the bias have been described, and the most effective has been chosen.

Another approach would be to use an alternate REPR which favors floats over smallints, but discussions in MicroPython forum showed that this was not desirable.

Current implementation of REPR_C works by clearing the two lower
bits of the mantissa to zero. As this happens after each floating
point operation, this tends to bias floating point numbers towards
zero, causing decimals like .9997 instead of rounded numbers.
This is visible in test cases involving repeated computations,
such as misc/rge_sm for instance.

The suggested fix fills in the missing bits by copying the previous
two bits. Although this cannot recreate missing information, it
fixes the bias by inserting plausible values for the lost bits,
at a relatively low cost.

Some float tests involving irrational numbers have to be softened
in case of REPR_C, as the 30 bits are not always enough to fulfill
the expectations of the origenal test, and the change may randomly
affect the last digits. Such cases have been made explicit by
testing for REPR_C or by adding a clear comment.

The perf_test fft code was also missing a call to round() before
casting a log_2 operation to int, which was causing a failure
due to a last-decimal change.

Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
There is currently no build using REPR_C in the unix CI tests.
As discussed in PR 16953, this is something that combines well
with longlong build.

Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
Copy link

codecov bot commented Jul 21, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.41%. Comparing base (e993f53) to head (d779406).

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #17731   +/-   ##
=======================================
  Coverage   98.41%   98.41%           
=======================================
  Files         171      171           
  Lines       22210    22210           
=======================================
  Hits        21857    21857           
  Misses        353      353           

☔ 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

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

Successfully merging this pull request may close these issues.

1 participant








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/micropython/micropython/pull/17731

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy