micropython / micropython/micropython
RP2: Floats smaller than 1e-37 printed wrongly (and subnormals treated as 0.0)
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 22.1k
- Forks
- 9k
- Avg merge
- 6d 4h
- Merged PRs (30d)
- 16
Description
In completing my function for showing the representation of floats for the case of subnormals:
def fp32_repr(x, bits=True):
import struct
a0 = int.from_bytes(struct.pack('f', x), 'little')
if bits:
return f'{(a0>>31)&1:01b} {((a0>>23) & 0xff):08b} {(a0 & 0x7fffff):023b}'
else:
sign = '-' if a0 >> 31 else ''
exp = ((a0>>23) & 0xff)
m = a0 & 0x7fffff
if 255 > exp > 0:
exp -= 150
m |= 0x800000
elif exp == 0: # subnormal
exp -= 149
elif exp == 255: # Inf or NaN
if m == 0:
return f'{sign}Inf'
else:
return 'NaN'
return f'{sign}{m} * 2**{exp}'
I noted that the RP2040 port (as of v1.22.0-preview.226.g0960d64d3 on 2023-12-06), after correction of the last float imprecision issue handles small floats differently than the other ports in that it treats subnormal representations as zero (0.0). This might be documented as a feature, not a bug:-), if correction seems too difficult.
A real bug is the printing of very small floats (< 1e-37, but still normal representation i.e. exponent bits > 0) as 1e-37.
This is demonstrated by the following code:
def irep2fp32(i):
import struct
return struct.unpack('f',i.to_bytes(4, 'little'))[0]
# With correct implementation of fp32: (including subnormal representation, where exp bits are 0)
# e.g. on Pyb 1.1 / Blackpill (stm32f411):
#
# int.from_bytes(struct.pack('f', 4.2e-41),'little') --> 29972 subnormal
# int.from_bytes(struct.pack('f', 4.2e-39),'little') --> 2997220 subnormal
# int.from_bytes(struct.pack('f', 3.2e-38),'little') --> 19806589 normal
# int.from_bytes(struct.pack('f', 1.2e-37),'little') --> 35870182 normal
print(irep2fp32(29972), irep2fp32(29972)*1000)
print(irep2fp32(2997220), irep2fp32(2997220)*1000)
print(irep2fp32(19806589), irep2fp32(19806589)*1000)
print(irep2fp32(35870182), irep2fp32(35870182)*1000)
print(3.2e-38)
On Blackpill and w600 boards (correct):
4.200112e-41 4.199972e-38
4.199999e-39 4.2e-36
3.2e-38 3.2e-35
1.2e-37 1.2e-34
3.2e-38
on esp8266 (30 bit):
4.20011e-41 4.19997e-38
4.2e-39 4.2e-36
3.2e-38 3.2e-35
1.2e-37 1.2e-34
3.2e-38
on Raspberry Pi Pico with RP2040 MicroPython v1.22.0-preview.226.g0960d64d3 on 2023-12-06
1e-37 0.0 <-- subnormal (4.2e-41), interpreted as 0.0 in calculation, but printed as 1e-37
1e-37 0.0 <-- subnormal (4.2e-39), interpreted as 0.0 in calculation, but printed as 1e-37
1e-37 3.2e-35 <-- first is incorrect, although not subnormal, probably only wrong print
1.2e-37 1.2e-34 <-- correct
1e-37 <-- wrong, bug
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the supplied irep2fp32 and printing examples on the RP2040, comparing them with the Blackpill or w600 results. Use the fp32_repr normal and subnormal cases as the expected behavior, then locate the RP2040 float conversion or formatting entry point; done means very small normal floats print accurately and the subnormal behavior is resolved or documented.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, python
- Domain
- embedded-iot
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100