-
-
Notifications
You must be signed in to change notification settings - Fork 8.5k
Add OBJ_REPR_E for full 32-bit float precision #18401
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
base: master
Are you sure you want to change the base?
Conversation
|
Code size report: |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #18401 +/- ##
=======================================
Coverage 98.38% 98.38%
=======================================
Files 171 171
Lines 22294 22294
=======================================
Hits 21933 21933
Misses 361 361 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Signed-off-by: Jeff Epler <[email protected]>
Signed-off-by: Jeff Epler <[email protected]>
|
@yoctopuce is there any tuning of the float to string conversion that can be done for this port? I noticed that for instance |
|
@jepler Interesting PR :-) I started on the same path back in June, but feedback on this idea was not very positive at that time: https://github.com/orgs/micropython/discussions/17566 Regarding your question, you can try to enable and see if that helps for these edge cases. |
Now, use of an identifier like `MP_CONST_FLOAT_e` introduces a floating point constant object. Signed-off-by: Jeff Epler <[email protected]>
|
I changed disable uctypes & use LONGINT_IMPL_LONGLONG in bothdiff --git a/ports/unix/variants/longlong/mpconfigvariant.h b/ports/unix/variants/longlong/mpconfigvariant.h
index d50d360b1f..7ac596085f 100644
--- a/ports/unix/variants/longlong/mpconfigvariant.h
+++ b/ports/unix/variants/longlong/mpconfigvariant.h
@@ -40,5 +40,8 @@
// Set base feature level.
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES)
+// Disable uctypes so size comparison with REPR_E is relevant
+#define MICROPY_PY_UCTYPES (0)
+
// Enable extra Unix features.
#include "../mpconfigvariant_common.h"
diff --git a/ports/unix/variants/repr_e/mpconfigvariant.h b/ports/unix/variants/repr_e/mpconfigvariant.h
index 82fd84c633..bf23592199 100644
--- a/ports/unix/variants/repr_e/mpconfigvariant.h
+++ b/ports/unix/variants/repr_e/mpconfigvariant.h
@@ -30,6 +30,9 @@
// for full float range & precision. Therefore this variant should be built
// using MICROPY_FORCE_32BIT=1
+// Use MICROPY_LONGINT_IMPL_LONGLONG so size comparison with longlong is relevant.
+#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG)
+
#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_E)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
diff --git a/ports/unix/variants/repr_e/mpconfigvariant.mk b/ports/unix/variants/repr_e/mpconfigvariant.mk
index 5765e86bc0..02f4f3842d 100644
--- a/ports/unix/variants/repr_e/mpconfigvariant.mk
+++ b/ports/unix/variants/repr_e/mpconfigvariant.mk
@@ -6,3 +6,5 @@ MICROPY_FORCE_32BIT := 1
MICROPY_PY_FFI := 0
RUN_TESTS_MPY_CROSS_FLAGS = --mpy-cross-flags='-march=x86 -msmall-int-bits=30'
+
+MPY_TOOL_FLAGS = -mlongint-impl longlong |
|
Is there any particular numeric workload it would be instructive to try, to make sure the improved accuracy is working? what sorts of test(s) should be written specifically for REPR_E? For instance should I test that float arithmetic on "modest numbers" doesn't do allocations? All bits from "struct.unpack" come throughA lengthy sum() exactly matches numpy.float32 arithmeticHeap/boxing behavior |
# Sum the harmonic series until an addend is so small it doesn't contribute
# to the sum.
#
# cpython: np.float32(15.403683) 4176757c
# repr_e: 15.403683 4176757c
# longlong: 13.218584 41537f50
import struct
import sys
if sys.implementation.name == "cpython":
from numpy import float32 as ff
else:
ff = float
f = ff("1")
g = 0
while True:
h = g + 1/f
if g == h: break
f += 1
g = h
print(repr(g), "%08x" % struct.unpack('I', struct.pack("f", g))) |
|
@jepler This is very clever. I think it would be worth including quite a bit more explanation in |
In Repr E, the full precision of 32-bit floating point values can be used, but absolute values smaller than 3.7252904e-09 or bigger than 5.36871e+08 are stored as boxed values, requiring allocations. Repr E reduces the width of small ints to 30 bits. Signed-off-by: Jeff Epler <[email protected]>
|
Good points @dhalbert , updated. (as well as some other fixes) |
@yoctopuce indeed it's quite a big change to lose precision of small ints. I otherwise agree with your comments on that discussion, that in some applications more float precision is better than more small ints being able to exist without heap allocation. So maybe your idea could be a new object representation (although not necessarily used by any ports here). I did actually suggest a related idea for this PR to @jepler, but my idea was to retain (30+1)-bits for small ints. @jepler I did not read the paper you cite, and I should have made this clearer in my email to you, but I was thinking to just use exactly the same object layout as the existing representation C (which has 31-bit small ints), that boxes qstr and immediate objects inside the float nan space. Then just create floats on the heap if they can't be exactly represented when truncating the least two bits. Does that make sense? |
|
@dpgeorge indeed, it's likely I misunderstood, because I had this paper in mind. What's interesting about the approach in the paper is that "presumably common floats" (ones within some range of exponents around 0) are all unboxed, while it's the ones with extreme exponents that get boxed. As opposed to just adding boxed floats to REPR_C, where 3/4 of all floats will end up boxed uniformly without respect to exponent. But, I just can't figure out a way to also keep 31 bit small ints while using an "add and roll" encoding. I can arrange for the tag bits of floats to be If that means this approach is not likely to be merged that's fine, I got a good education in object representation out of it. Assuming I understand you now, I can come back and try the approach that strictly extends REPR_C instead in a fresh PR. |
Summary
Add a new object representation called OBJ_REPR_E.
This work is loosely inspired by Float Self-Tagging but the shift & tag values are not any of those proposed in the paper.
Testing
I locally ran the testsuite. Some native tests fail, which I'll have to resolve one way or another. It may be #18108 because the small int size is 30 bits, not 31.
Trade-offs and Alternatives
I did not see a way to organize the new repr while retaining 31-bit ints. This means we hit #18105 and #18108 -- no uctypes, and no mpy-compiled native/viper code.
It adds about +1.5kB.
This builds on #18396