Skip to content

py/formatfloat: Format ALL whole-number floats without decimal cruft. #8905

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

Closed
wants to merge 1 commit into from

Conversation

dpwe
Copy link
Contributor

@dpwe dpwe commented Jul 13, 2022

Formerly, py/formatfloat would print whole numbers inaccurately
with nonzero digits beyond the decimal place. This resulted
from its strategy of successive scaling of the argument by 0.1
which cannot be exactly represented in floating point. This
change avoids scaling until the value is smaller than 1, so
all whole numbers print with zero fractional part.

This is to fix issue #4212.

This PR is an alternative to #8901. It uses a different algorithm, using
floating-point arithmetic instead of uint32_t. This makes it able to
work for all whole values (not only those smaller than 2^32). It may
also reduce code size (to be verified).

Signed-off-by: Dan Ellis dan.ellis@gmail.com

@dpwe
Copy link
Contributor Author

dpwe commented Jul 13, 2022

I'm happy with the code here, but it's failing the float_parse_doubleprec test for unix port / nanbox. Any advice on how I can debug this?

Also, I'd be interested to see the code size comparisons.

@dpgeorge dpgeorge added the py-core Relates to py/ directory in source label Jul 13, 2022
@dpgeorge
Copy link
Member

This is the size-diff I get for this PR:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:  +176 +0.034% 
unix nanbox:  +144 +0.031% 
      stm32:  +156 +0.039% PYBV10
     cc3200:    +0 +0.000% 
    esp8266:  +256 +0.037% GENERIC
      esp32:  +464 +0.031% GENERIC
     mimxrt:  +144 +0.040% TEENSY40
 renesas-ra:  +152 +0.024% RA6M2_EK
        nrf:  +144 +0.077% pca10040
        rp2:  +160 +0.031% PICO
       samd:  +156 +0.111% ADAFRUIT_ITSYBITSY_M4_EXPRESS

@dpgeorge
Copy link
Member

I'm happy with the code here, but it's failing the float_parse_doubleprec test for unix port / nanbox. Any advice on how I can debug this?

You'll need to build the unix port with make VARIANT=nanbox. Then run ./micropython-nanbox <test.py>.

The thing that fails is formatting the following numbers (represented here as bytes objects so that parsing is not part of the test):

import array
print(array.array('d', b'Zb\xd7\xd7\x18\xe7ti'))
print(array.array('d', b'}\xc3\x94%\xadI\xb2T'))

The result on CPython and unix micropython (standard build with this PR) is:

array('d', [1e+200])
array('d', [1e+100])

But on micropython-nanbox (with this PR) it's:

array('d', [9.999999999999999e+199])
array('d', [9.999999999999999e+99])

@dpwe
Copy link
Contributor Author

dpwe commented Jul 13, 2022

Thanks for the help. Unfortunately, VARIANT=nanbox doesn't link on my dev system (MacOS 10.15.17, "warning: The i386 architecture is deprecated for macOS") although I can try it on my linode.

Do you happen to know what is different about nanbox? It's as if it's failing some fairly specific floating-point comparisons. I tried Googling nanbox, and I understand the concept of nan-boxing, but I'm not clear how it's used in MicroPython, or why it would affect non-NaN floating point operations.

@codecov-commenter
Copy link

codecov-commenter commented Jul 14, 2022

Codecov Report

Merging #8905 (af11762) into master (b878fc0) will increase coverage by 0.00%.
The diff coverage is 100.00%.

@@           Coverage Diff           @@
##           master    #8905   +/-   ##
=======================================
  Coverage   98.33%   98.34%           
=======================================
  Files         157      157           
  Lines       20348    20360   +12     
=======================================
+ Hits        20010    20023   +13     
+ Misses        338      337    -1     
Impacted Files Coverage Δ
py/formatfloat.c 100.00% <100.00%> (+0.58%) ⬆️
py/objnamedtuple.c 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b878fc0...af11762. Read the comment docs.

@dpwe
Copy link
Contributor Author

dpwe commented Jul 14, 2022

Hmm, I was so sure I had fixed it!

It turns out the "difference" may lie partially in the parsing code rather than the formatting code. On the plain build on my Mac, float("9" * 400 + "e-200") results in the float64 0x6974e718d7d7625c but on the nanbox build it is 0x6974e718d7d7625a (2 eps smaller). In fact, the nanbox value is closer to the specified value.

0x6974e718d7d7625a -> 99999999999999996973312221251036165947450327545502362648241750950346848435554075534196338404706251868027512415973882408182135734368278484639385041047239877871023591066789981811181813306167128854888448
0x6974e718d7d7625c -> 100000000000000030966143761524130482081095546903494511742201285480389933199978920360024169499249787174427657390648165217099222854144343466820462650820503200080302232128380506216383146771333146885488640

However, it's also true that the test case you sent (corresponding to the nanbox value, ...625a) renders differently on the two builds. This appears to be because, on nanbox but not on plain unix, the expression (f >= next_u_base) evaluates to false if both f and next_u_base are float64s equal to that value -- so they should compare equal, but don't. (I see it for several powers of 10, e.g. 1e23 0x4950912855330343670. But in that case, the rounding saves us). Possibly worth raising a bug with the nanbox FP routines?

Overall, I don't think the test failure is a "bug" per se, it's just an imprecision. And the fact that it passed under the old code was .. fluke?

If we changed the print in the test to use 15 sig figs, I believe it would work. But asking you to change the test to pass my code feels .. problematic.

@dpgeorge
Copy link
Member

This appears to be because, on nanbox but not on plain unix, the expression (f >= next_u_base) evaluates to false if both f and next_u_base are float64s equal to that value -- so they should compare equal, but don't.

Yes I'm also seeing this behaviour, that two values which have the same binary representation are not comparing equal.

I think the issue is that on x86 the internal precision of double operations is 80 bits. And so even though they are equivalent 64-bit doubles, their 80-bit representations differ.

I could fix it by forcing a truncation of the 80-bit number:

@@ -265,9 +270,10 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch
         // mantissa.
         FPTYPE u_base = FPCONST(1.0);
         for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++) {
-            if (f >= (u_base * *pos_pow)) {
+            volatile double xx = u_base * *pos_pow;
+            if (f >= xx) {
                 e += e1;
-                u_base *= *pos_pow;
+                u_base = xx;
             }
         }

That's not very satisfactory.

Can you change the >= in that comparison to >? Then it won't rely on any edge behaviour. Then you may need to adjust other bits of code to compensate for that change.

Alternatively, go back to the integer version #8901.

@dpwe
Copy link
Contributor Author

dpwe commented Jul 14, 2022

I tried testing for the equality case separately, after the initial loop, by subtracting the new base 10 times and seeing if the result was >= 0, but it failed the same way - again, presumably, because the compiler is optimizing by keeping the intermediate result in an 80 bit register.

The equality case is crucial here, because it's the difference between trying to format 1e100 as 1e100 vs. 9.99999..e99. It turns out that the cumulative errors when printing 9.9999..e99 to 16 significant figures are such that it doesn't round up to 1e100 (because the value "looks like" 9.9999999999999987334e+199, so rounds up at 16 sf to 9.9999...).

Given that we're now coding around deviations from the ideal model, I think "volatile" is as good a solution as any - at least the reader knows that it's not about details of the math, but something in the compilation.

It's so interesting that this only came up for nanbox. I assume it's non-IEEE-compliant to do comparisons without truncating down to 64 bits; maybe the deal with nanbox is that it's already noncompliant, so they pull in other optimizations too?

@dpwe dpwe force-pushed the pure-float branch 3 times, most recently from dea3d5f to 7e5dc50 Compare July 14, 2022 21:59
@dpwe
Copy link
Contributor Author

dpwe commented Jul 14, 2022

I got a little deeper into the code and simplified it, looking for code size reductions.

@dpgeorge
Copy link
Member

It's so interesting that this only came up for nanbox. I assume it's non-IEEE-compliant to do comparisons without truncating down to 64 bits; maybe the deal with nanbox is that it's already noncompliant, so they pull in other optimizations too?

Nan-boxing mode is defined at our (MicroPython) end, it's a 32-bit x86 build with 64-bit objects.

The same bug appears in normal 32-bit builds of unix MicroPython. The reason the CI didn't see it (which does build and test 32-bit unix MicroPython) is because the parsing of numbers is slightly different in nanbox vs non-nanbox (as noted up above).

You can see the error on a standard 32-bit build by using the test that gives the explicit representation of the double:

import array
print(array.array('d', b'Zb\xd7\xd7\x18\xe7ti'))
print(array.array('d', b'}\xc3\x94%\xadI\xb2T'))

We should definitely add this exact test to the test suite.

If we go the route using volatile, it's only needed on x86 in 32-bit build mode, so can be guarded with #if ....

@dpgeorge
Copy link
Member

Current code size diff is now:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% 
unix nanbox:    +0 +0.000% 
      stm32:   +36 +0.009% PYBV10
     cc3200:    +0 +0.000% 
    esp8266:  +104 +0.015% GENERIC
      esp32:  -196 -0.013% GENERIC
     mimxrt:   +16 +0.004% TEENSY40
 renesas-ra:   +32 +0.005% RA6M2_EK
        nrf:   +24 +0.013% pca10040
        rp2:   +24 +0.005% PICO
       samd:   +36 +0.026% ADAFRUIT_ITSYBITSY_M4_EXPRESS

That's surprising that unix is exactly 0... I double checked it and it seems to be true!

py/formatfloat.c Outdated
// Use "volatile" to force the result to be stored in a float64.
// Without this, nanbox build would sometimes fail to report
// "equality" leading to weird results.
volatile FPTYPE next_u = u_base * *pos_pow;
Copy link
Member

Choose a reason for hiding this comment

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

Please guard the volatile with #if defined(__i386__)

# represented by a float32 and that will also fit within a (signed) int32.
# The upper bound of our integer-handling code is actually double this,
# but that constant might cause trouble on systems using 32 bit ints.
print("{:f}".format(2147483520))
Copy link
Member

Choose a reason for hiding this comment

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

Please add the 2 test cases using the explicit representation and array.array

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Won't that fail on builds with

#define MICROPY_FLOAT_IMPL          (MICROPY_FLOAT_IMPL_FLOAT)

I guess there's some way to gate or condition tests based on build conditions. I'll look into it.

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'm going with a separate tests/float/float_format_int_doubleprec.py, which I'll add to the if upy_float_precision < 64 skips in run-tests.py.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good!

@dpwe dpwe force-pushed the pure-float branch 4 times, most recently from 3defa4d to 9a929b2 Compare July 15, 2022 05:01
@dpwe
Copy link
Contributor Author

dpwe commented Jul 15, 2022

This looks good, right?

I regret to say there are still some integer powers of ten that don't work out perfectly, e.g.

>>> print(float(1e33))
9.999999999999998e+32

(33 is the smallest, and there are only 10 below 1e100). I'm not sure how this is coming about, I guess because there are differences in how the parsing constructs the float, and how we build it in the formatting code (by multiplying-up factors of 10**(2**i)).

The unmodified code does this too, with the lowest being at 1e23. However, there are only a total of 7 problem cases below 1e100.

@dpwe dpwe force-pushed the pure-float branch 2 times, most recently from 0a2a26e to d4499d9 Compare July 15, 2022 17:39
@dpwe
Copy link
Contributor Author

dpwe commented Jul 15, 2022

I was able to fix printing of all values of 1eX by implementing fp_ge_eps(x,y) which returns (x >= (y - 2eps)) where eps is the unit value in the mantissa. I found that by allowing for "off-by-2" differences between 1eX as calculated by float(s) and 1eX as calculated within formatfloat, all 1eX values were covered. fp_ge_eps operates on the binary representation of the float/double via the floatbits union (now also for doubles).

The consequence is that some values that are legitimately just smaller than 10^X are now rounded up too aggressively. However, the closest this comes to affecting integers is around 10^16:

>>> d = 10000000000000000 - 2
>>> print(d)
9999999999999998
>>> print("{:.16f}".format(float(d)))
10000000000000000.0000000000000

Even at this point, we can't exactly represent every integer since eps=2. However, we can exactly represent 10^16 - 2, but instead it gets rounded up to 10^16. It's not perfect, but I think it's OK as the price we pay for clean printing of 10^X values.

I added a test of printing all values of 10^X up to 10^32 (largest that remains finite in float32). This worked at 12 significant figures on all architectures except qemu-arm, which for some reason had serious problems with 10^11. I had to reduce the resolution to 7 s.f. to make it pass.

@dpwe
Copy link
Contributor Author

dpwe commented Jul 15, 2022

(oh and the fp_ge_eps() fix allowed us to drop the volatile kluge introduced for i386/nanbox).

@dpwe
Copy link
Contributor Author

dpwe commented Jul 15, 2022

To illustrate the 'aggressive rounding up' problem more completely, consider the following code fragment:

>>> d = int(1e16)
>>> for i in range(-10, 10):
...     print(d + i, "{:f}".format(float(d + i)))
... 
9999999999999990 9999999999999990.000000
9999999999999991 9999999999999992.000000
9999999999999992 9999999999999992.000000
9999999999999993 9999999999999992.000000
9999999999999994 9999999999999994.000000
9999999999999995 10000000000000000.000000
9999999999999996 10000000000000000.000000
9999999999999997 10000000000000000.000000
9999999999999998 10000000000000000.000000
9999999999999999 10000000000000000.000000
10000000000000000 10000000000000000.000000
10000000000000001 10000000000000000.000000
10000000000000002 10000000000000002.000000
10000000000000003 10000000000000004.000000
10000000000000004 10000000000000004.000000
10000000000000005 10000000000000004.000000
10000000000000006 10000000000000006.000000
10000000000000007 10000000000000008.000000
10000000000000008 10000000000000008.000000
10000000000000009 10000000000000008.000000

Notice the jump from ...994.000... to ...000.000.... We could have hoped for 996 and 998 in there.

However, compare the output from the current micropython:master code:

9999999999999990 9999999999999992.894573
9999999999999991 9999999999999992.894573
9999999999999992 9999999999999992.894573
9999999999999993 9999999999999992.894573
9999999999999994 9999999999999994.670929
9999999999999995 9999999999999998.223643
9999999999999996 9999999999999998.223643
9999999999999997 9999999999999998.223643
9999999999999998 10000000000000000.000000
9999999999999999 10000000000000000.000000
10000000000000000 10000000000000000.000000
10000000000000001 10000000000000000.000000
10000000000000002 10000000000000002.220446
10000000000000003 10000000000000004.440892
10000000000000004 10000000000000004.440892
10000000000000005 10000000000000004.440892
10000000000000006 10000000000000006.661338
10000000000000007 10000000000000008.881784
10000000000000008 10000000000000008.881784
10000000000000009 10000000000000008.881784

It manages to nail the 10^16 value (as it happens, although it doesn't for 10^23, 10^25, or 10^29), but all those other non-integer values - ugh.

@dpwe dpwe changed the title py/formatfloat: Format ALL whole-number floats exactly. py/formatfloat: Format ALL whole-number floats without decimal cruft. Jul 15, 2022
@dpwe
Copy link
Contributor Author

dpwe commented Jul 25, 2022

Great.

I rebased to master, changed to using mp_float_union_t, and added an expected output tests/float/float_format_ftoe.py.exp. Tests are passing, I think this is ready to go.

@dpgeorge
Copy link
Member

Running the new tests on bare-metal targets shows a few issues:

  • on stm32 (PYBv1.0) and esp32 (both single precision float) the new float_format_ftoe.py test doesn't pass, it outputs: 8.888890e+32 instead of 8.888889e+32. These MCUs have hardware single precision float.

  • on rp2 (PICO) "{:.7g}".format(1e31) gives '9.999999e+30' instead of '1e31'. This MCU uses software routines for the single precision float.

Otherwise, the rest of the tests pass on these two boards (and they all pass on PYBD_SF6, which is stm32 with hardware double precision floats).

We need to get these tests passing on these bare-metal targets. For the first case could it use "%.5f" instead of "%f", or would that test something different? For the second case can we just range up to 1e30 instead of 1e31?

@dpgeorge
Copy link
Member

New size diff is slightly better than before:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:   +32 +0.005% 
unix nanbox:   +64 +0.011% 
      stm32:   +36 +0.009% PYBV10
     cc3200:    +0 +0.000% 
    esp8266:   +36 +0.005% GENERIC
      esp32:  -156 -0.010% GENERIC
     mimxrt:   +40 +0.011% TEENSY40
 renesas-ra:   +32 +0.005% RA6M2_EK
        nrf:   +16 +0.009% pca10040
        rp2:    +0 +0.000% PICO
       samd:   +36 +0.026% ADAFRUIT_ITSYBITSY_M4_EXPRESS

Formerly, py/formatfloat would print whole numbers inaccurately
with nonzero digits beyond the decimal place.  This resulted
from its strategy of successive scaling of the argument by 0.1
which cannot be exactly represented in floating point.  This
change avoids scaling until the value is smaller than 1, so
all whole numbers print with zero fractional part.

This is to fix issue micropython#4212

Signed-off-by: Dan Ellis dan.ellis@gmail.com
@dpwe
Copy link
Contributor Author

dpwe commented Jul 26, 2022

  • I changed tests/float_format_ftoe.py to use %.2f which I verified still exposes the original bug.
  • I limited the range of printing powers of ten in tests/float_format_ints.py to stop at 10^30.

@dpgeorge
Copy link
Member

Thanks for fixing the tests, I checked on rp2 and stm32 and they are indeed now fixed.

Rebased and merged in f9cbe6b

This is a really nice improvement to have, it will definitely be appreciated by users. And a bonus that the final code size increase was minimal. Thanks very much @dpwe !

@dpwe
Copy link
Contributor Author

dpwe commented Jul 28, 2022

I'm not sure how passionate we feel about code size vs. execution speed. I've continued playing with this (after looking at parsenum.c to see why we were experiencing the off-by-2eps problem) and I can get rid of the lookup tables altogether, saving ~144 bytes in float64 builds, at the cost of multiple calls to pow(10, i), which I expect is a lot slower. That removes the need for fp_ge_eps, but does expose problems with the parsing of some decimals (see my comment on #5832, as specifically revealed in tests/float/float_parse.py).

@dpwe
Copy link
Contributor Author

dpwe commented Jul 29, 2022

I have a pow(10, i)-based approach, along with a fix to py/parsenum.c to ignore trailing zeros in float value literals, implemented in: master...dpwe:micropython:format_float_with_pow . It's able to directly predict the base-10 exponent from the base-2 exponent in mp_float_union_t, so it doesn't make too many calls to pow(). It eliminates the aggressive-rounding behavior, since the 10^X values in formatfloat directly match the ones used in parsenum.

@dpgeorge
Copy link
Member

If you want to make a PR for that change, then we can evaluate it.

dpwe added a commit to dpwe/micropython that referenced this pull request Jul 29, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead eliminates the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to ensure the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
dpwe added a commit to dpwe/micropython that referenced this pull request Jul 29, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead eliminates the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to ensure the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
dpwe added a commit to dpwe/micropython that referenced this pull request Jul 29, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead eliminates the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to ensure the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
dpwe added a commit to dpwe/micropython that referenced this pull request Jul 29, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead removes the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to ensure the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
dpwe added a commit to dpwe/micropython that referenced this pull request Aug 2, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead removes the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to make the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
dpwe added a commit to dpwe/micropython that referenced this pull request Aug 2, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead removes the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to make the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
dpwe added a commit to dpwe/micropython that referenced this pull request Aug 2, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead removes the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to make the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
dpwe added a commit to dpwe/micropython that referenced this pull request Aug 2, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead removes the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to make the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
dpwe added a commit to dpwe/micropython that referenced this pull request Aug 2, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead removes the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to make the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
dpwe added a commit to dpwe/micropython that referenced this pull request Aug 2, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead removes the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to make the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
dpwe added a commit to dpwe/micropython that referenced this pull request Aug 4, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead removes the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to make the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
dpwe added a commit to dpwe/micropython that referenced this pull request Aug 4, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead removes the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to make the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
dpwe added a commit to dpwe/micropython that referenced this pull request Aug 11, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead removes the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to make the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
dpwe added a commit to dpwe/micropython that referenced this pull request Aug 11, 2022
Formerly, ```formatfloat``` struggled with formatting exact powers of 10
as created by ```parsenum``` because of small differences in how those
powers of 10 were calculated: ```formatfloat``` multiplied together a few
values of 10^(2^Y) from a lookup table, but ```parsenum``` used
```pow(10, X)```.  This was mitigated in micropython#8905 by adding 2eps of extra
margin when comparing values ("aggressive rounding up").

This patch instead removes the discrepency by using ```pow()``` in
```formatfloat``` also.  This eliminates the need for the 2eps margin, as
well as the lookup tables.  It is likely slower to run, however.

In addition, this patch directly estimates the power-of-10 exponent from
the power-of-2 exponent in the floating-point representation.

This change surfaced a previously-noted problem with parsing numbers
including trailing zeros.  The fix to that problem, micropython#8980, is included
in this PR to make the tests pass.

Signed-off-by: Dan Ellis <dan.ellis@gmail.com>
tannewt added a commit to tannewt/circuitpython that referenced this pull request Feb 13, 2024
Jpegio fixes: swap order of size and example code Argument
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.

3 participants