From 335c0160474bddb2a0174c96d4bc887cd6c97c4a Mon Sep 17 00:00:00 2001 From: kapouchima <8651984+kapouchima@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:24:59 +0200 Subject: [PATCH 1/2] libc: carry the rounding out of the leading digit in %f float_to_str() rounds by adding half a unit of the last printed digit to the normalised mantissa, but only after the layout (decimal exponent, "0." prefix, digit count) is fixed. When that addition carries out of the leading digit the first digit comes out as 10, which the emit loop clamps to 9, so the carry is lost: with %.2f, 9.9976 prints as "9.00", 99.9976 as "90.00", 0.996 as "0.90" and 0.006 as "0.00". Seen on a PMD board as a phantom 1 degree encoder disagreement: the absolute encoder at 455 counts (9.9976 deg) printed "9.00 deg" next to a servo position of 10.00, and the position seed log printed "90.00 deg (4551 counts)" for 99.998 deg. Round before the layout, and when the mantissa reaches 1.0 divide it by 10 and bump the exponent so the extra leading digit is printed. The clamp stays as a safety net; it no longer triggers. A host model of the algorithm checked against a decimal half-up reference over 200 000 random values at 2, 3 and 6 decimals: 388 mismatches before, none after. On the PMD mock (STM32H725) the same board prints "455 counts 10.00 deg" where it printed "9.00 deg", and the seed at 4552 counts prints "100.02 deg". Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01ExwkpjVKyyM5HBhzois9fr --- src/lib/libc/stdio.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/lib/libc/stdio.c b/src/lib/libc/stdio.c index 46f2d942..87864a5b 100644 --- a/src/lib/libc/stdio.c +++ b/src/lib/libc/stdio.c @@ -341,6 +341,20 @@ float_to_str(fmtcb_t *cb, void *aux, size_t total = 0; int significands = fp->decimals > 0 ? fp->decimals : 6; + // Round half up at the last printed digit before the layout is fixed: + // a carry out of the leading digit adds one (9.998 -> "10.00", not "9.00") + int digits = significands + e10; + if(digits >= 0) { + uint64_t r = 1ULL << 59; + for(int i = 0; i < digits; i++) + r /= 10; + mantissa += r; + if(mantissa >= (1ULL << 60)) { + mantissa /= 10; + e10++; + } + } + int chars = flt_count_output_chars(sign, e10, significands, fp); int pad = fp->width - chars; @@ -368,11 +382,6 @@ float_to_str(fmtcb_t *cb, void *aux, } } - uint64_t r = 1ULL << 59; - for(int i = 0; i < significands; i++) - r /= 10; - mantissa += r; - for(int i = 0; i < significands; i++) { mantissa *= 10; From b490889062567e36f34add38eb0b43316b3f0f19 Mon Sep 17 00:00:00 2001 From: kapouchima <8651984+kapouchima@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:31:01 +0200 Subject: [PATCH 2/2] stm32f4: initialise the crashlog buffer like g4 and h7 4dc901e6 made crashlog_stream.buf a runtime value that the platform sets with crashlog_init(), and updated stm32g4 and stm32h7 but not stm32f4_ccm.c, which includes the same source. The F4 boards therefore fail to build (crashlog_init defined but not used, -Werror), and had it built the crash log would have been silently disabled there because the buffer stays NULL. Call crashlog_init() with the CCM address before crashlog_recover(), exactly as stm32h7.c does. Builds stm32f407g-disc1, stm32f405-feather and stm32h7-nucleo144 again. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01ExwkpjVKyyM5HBhzois9fr --- src/platform/stm32f4/stm32f4_ccm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/stm32f4/stm32f4_ccm.c b/src/platform/stm32f4/stm32f4_ccm.c index fb18d770..edfec5f8 100644 --- a/src/platform/stm32f4/stm32f4_ccm.c +++ b/src/platform/stm32f4/stm32f4_ccm.c @@ -37,5 +37,6 @@ stm32f4_ccm_init(void) heap_add_mem(0x10000000 + sizeof(cpu_t), CRASHLOG_ADDR, MEM_TYPE_LOCAL, 5); + crashlog_init((void *)CRASHLOG_ADDR); crashlog_recover(); }