micropython / micropython/micropython

RP2: Floats smaller than 1e-37 printed wrongly (and subnormals treated as 0.0)

Open
#13,210 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug port-rp2
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.