For 8-bit microcontrollers like AVRs, I'd suggest repeated substraction instead. For example,
// SPDX-License-Identifier: CC0-1.0
#include <stdint.h>
static const uint32_t u32_dec[20] = {
UINT32_C(1),
UINT32_C(3),
UINT32_C(10),
UINT32_C(30),
UINT32_C(100),
UINT32_C(300),
UINT32_C(1000),
UINT32_C(3000),
UINT32_C(10000),
UINT32_C(30000),
UINT32_C(100000),
UINT32_C(300000),
UINT32_C(1000000),
UINT32_C(3000000),
UINT32_C(10000000),
UINT32_C(30000000),
UINT32_C(100000000),
UINT32_C(300000000),
UINT32_C(1000000000),
UINT32_C(3000000000),
};
// Little-endian byte order
#define BYTEINDEX(n, m) (n)
// Big-endian byte order
// #define BYTEINDEX(n, m) ((m)-(n))
// Little-endian byte order
static const uint8_t u32_bcd[20][2] = {
{ BYTEINDEX(0, 4), 0x01 },
{ BYTEINDEX(0, 4), 0x03 },
{ BYTEINDEX(0, 4), 0x10 },
{ BYTEINDEX(0, 4), 0x30 },
{ BYTEINDEX(1, 4), 0x01 },
{ BYTEINDEX(1, 4), 0x03 },
{ BYTEINDEX(1, 4), 0x10 },
{ BYTEINDEX(1, 4), 0x30 },
{ BYTEINDEX(2, 4), 0x01 },
{ BYTEINDEX(2, 4), 0x03 },
{ BYTEINDEX(2, 4), 0x10 },
{ BYTEINDEX(2, 4), 0x30 },
{ BYTEINDEX(3, 4), 0x01 },
{ BYTEINDEX(3, 4), 0x03 },
{ BYTEINDEX(3, 4), 0x10 },
{ BYTEINDEX(3, 4), 0x30 },
{ BYTEINDEX(4, 4), 0x01 },
{ BYTEINDEX(4, 4), 0x03 },
{ BYTEINDEX(4, 4), 0x10 },
{ BYTEINDEX(4, 4), 0x30 },
};
void binary32_to_bcd(uint8_t dst[5], uint32_t src)
{
dst[0] = dst[1] = dst[2] = dst[3] = dst[4] = 0;
int8_t i = 20;
while (i-->0)
while (src >= u32_dec[i]) {
src -= u32_dec[i];
dst[u32_bcd[i][0]] += u32_bcd[i][1];
}
}
takes about 258 bytes of Flash on AVR (gcc-5.4.0 -O2 -mmcu=atmega32u4), with the slowest case being 2888888888, 38 iterations of the inner loop total.
If you don't mind increasing the slowest case to 3999999999, 85 iterations of the inner loop total, you can use
// SPDX-License-Identifier: CC0-1.0
#include <stdint.h>
static const uint32_t u32_dec[10] = {
UINT32_C(1),
UINT32_C(10),
UINT32_C(100),
UINT32_C(1000),
UINT32_C(10000),
UINT32_C(100000),
UINT32_C(1000000),
UINT32_C(10000000),
UINT32_C(100000000),
UINT32_C(1000000000),
};
// Little-endian byte order
#define BYTEINDEX(n, m) (n)
// Big-endian byte order
// #define BYTEINDEX(n, m) ((m)-(n))
// Little-endian byte order
static const uint8_t u32_bcd[10][2] = {
{ BYTEINDEX(0, 4), 0x01 },
{ BYTEINDEX(0, 4), 0x10 },
{ BYTEINDEX(1, 4), 0x01 },
{ BYTEINDEX(1, 4), 0x10 },
{ BYTEINDEX(2, 4), 0x01 },
{ BYTEINDEX(2, 4), 0x10 },
{ BYTEINDEX(3, 4), 0x01 },
{ BYTEINDEX(3, 4), 0x10 },
{ BYTEINDEX(4, 4), 0x01 },
{ BYTEINDEX(4, 4), 0x10 },
};
void binary32_to_bcd(uint8_t dst[5], uint32_t src)
{
dst[0] = dst[1] = dst[2] = dst[3] = dst[4] = 0;
int8_t i = sizeof u32_dec / sizeof u32_dec[0];
while (i-->0)
while (src >= u32_dec[i]) {
src -= u32_dec[i];
dst[u32_bcd[i][0]] += u32_bcd[i][1];
}
}
which compiles to 198 bytes of flash (gcc-5.4.0, -O2 -mmcu=atmega32u4).