Bug observed IRL, AI determined bug report, seems plausible cause.
No, it has a correctness bug around the decimal placement, plus a couple of smaller concerns.
The main bug: missing zero padding when k < 10^precision.
The loop only inserts the . (and a leading '0') once decimals reaches precision. If k runs out of digits before that, the decimal point is never written and no zeros are padded. Concrete cases with precision = 2:
k = 5 → you get "5", should be "0.05"
k = 9 → you get "9", should be "0.09"
k = 50 → works: "0.50" ✓
k = 500 → works: "5.00" ✓
So it's correct only when k >= 10^(precision-1). For inputs below that threshold it silently drops the decimal point and the leading digits.
The k == 0 early return also ignores precision.
It just writes '0' and returns, so int_to_string(0, 2, NULL) gives "0" instead of "0.00". Whether that matters depends on your contract for the function, but it's inconsistent with the non-zero path.
No explicit NUL terminator.
The function never writes '\0'. It works today only because:
- The static
internal_buf is zero-initialized and position 20 is never overwritten (the loop always writes to positions < buf_len).
- A caller-supplied
buf must already be NUL-terminated at index buf_len, and must be at least buf_len + 1 bytes — that precondition isn't documented anywhere.
I'd write buf[buf_len] = '\0'; explicitly at the top. It's free and removes a landmine.
Minor stuff:
_int_to_string — identifiers beginning with an underscore at file scope are reserved by the C standard (C11 §7.1.3). Use a trailing underscore or a module prefix instead.
- The static scratch buffer makes this non-reentrant and means two overlapping calls with
buf == NULL will stomp on each other. Probably fine in a single-threaded embedded context, but worth noting.
decimals could be uint8_t to match precision and avoid the signed/unsigned mix in the comparison (the compiler will promote, so it's not a real bug — just tidier).
A fix for the padding bug is to continue the loop (or a second loop) until both k == 0 and decimals > precision:
while (k > 0 || decimals <= precision) {
char decimal = 0x30 + (k % 10);
k /= 10;
*ptr-- = decimal;
decimals++;
if (decimals == precision)
*ptr-- = '.';
}
That naturally handles k == 0 with any precision (producing "0.00" etc.), so you can also drop the if (k == 0) early return entirely. Trace it for k=5, precision=2: writes '5', '0', ., '0' → "0.05". ✓
Bug observed IRL, AI determined bug report, seems plausible cause.
No, it has a correctness bug around the decimal placement, plus a couple of smaller concerns.
The main bug: missing zero padding when
k < 10^precision.The loop only inserts the
.(and a leading'0') oncedecimalsreachesprecision. Ifkruns out of digits before that, the decimal point is never written and no zeros are padded. Concrete cases withprecision = 2:k = 5→ you get"5", should be"0.05"k = 9→ you get"9", should be"0.09"k = 50→ works:"0.50"✓k = 500→ works:"5.00"✓So it's correct only when
k >= 10^(precision-1). For inputs below that threshold it silently drops the decimal point and the leading digits.The
k == 0early return also ignoresprecision.It just writes
'0'and returns, soint_to_string(0, 2, NULL)gives"0"instead of"0.00". Whether that matters depends on your contract for the function, but it's inconsistent with the non-zero path.No explicit NUL terminator.
The function never writes
'\0'. It works today only because:internal_bufis zero-initialized and position 20 is never overwritten (the loop always writes to positions <buf_len).bufmust already be NUL-terminated at indexbuf_len, and must be at leastbuf_len + 1bytes — that precondition isn't documented anywhere.I'd write
buf[buf_len] = '\0';explicitly at the top. It's free and removes a landmine.Minor stuff:
_int_to_string— identifiers beginning with an underscore at file scope are reserved by the C standard (C11 §7.1.3). Use a trailing underscore or a module prefix instead.buf == NULLwill stomp on each other. Probably fine in a single-threaded embedded context, but worth noting.decimalscould beuint8_tto matchprecisionand avoid the signed/unsigned mix in the comparison (the compiler will promote, so it's not a real bug — just tidier).A fix for the padding bug is to continue the loop (or a second loop) until both
k == 0anddecimals > precision:That naturally handles
k == 0with any precision (producing"0.00"etc.), so you can also drop theif (k == 0)early return entirely. Trace it fork=5, precision=2: writes'5','0',.,'0'→"0.05". ✓