How to do rate check in the kernel?

Seems the printk() do not support to show the float number by format “%f”, I have a workaround solution as showed below, is there are normal solution to print rate check data?

static void rate_check(void) {

int cycle = 100;
float second = 0.12;
float rate = 0;
uint32_t rate_h;
uint32_t rate_l;

rate = cycle/second;

#if 0
printk(KERN_INFO “speed >>> rate(times/s): %f\n”, rate);
#else
rate_h = (uint32_t)rate;
rate_l = ((uint32_t)(rate * 100))%100;
printk(KERN_INFO “speed >>> rate(times/s): %d.%02d\n”, rate_h, rate_l);
#endif
return;
}

Hello @dingchaojie123

Your observation is correct - the kernel’s printk() doesn’t support floating point format specifiers like %f directly. This is by design since floating point operations in kernel space can be problematic for several reasons:

  1. Using floating point registers in kernel context requires saving and restoring FPU state, which is expensive
  2. The kernel needs to remain lightweight and avoid floating point operations where possible
  3. Not all architectures have FPU hardware support

Your workaround of splitting the number into integer and decimal parts is actually a common and valid approach. However, here are some alternative solutions:

  1. Use fixed-point arithmetic:
static void rate_check(void) {
    /* Use fixed point with 2 decimal places (multiply by 100) */
    const int PRECISION = 100;
    int cycle = 100;
    int second_fixed = 12; /* 0.12 * 100 */
    
    /* Calculate rate in fixed point */
    int rate_fixed = (cycle * PRECISION) / second_fixed;
    
    /* Print the result: divide by PRECISION for whole part,
       modulo for decimal part */
    printk(KERN_INFO "speed >>> rate(times/s): %d.%02d\n", 
           rate_fixed / PRECISION, rate_fixed % PRECISION);
}
  1. Use the kernel’s built-in fixed-point division:
#include <linux/math64.h>

static void rate_check(void) {
    int cycle = 100;
    int second_ms = 120; /* 0.12 seconds in milliseconds */
    
    /* Calculate rate using div_s64 for better precision */
    s64 rate = div_s64((s64)cycle * 1000, second_ms);
    
    printk(KERN_INFO "speed >>> rate(times/s): %lld\n", rate);
}
  1. If you need more precise decimal printing, you can use the kernel’s div_u64_rem():
#include <linux/math64.h>

static void rate_check(void) {
    u64 cycle = 100;
    u32 second_ms = 120; /* 0.12 seconds in milliseconds */
    u32 remainder;
    
    /* Calculate whole number part */
    u64 rate = div_u64_rem(cycle * 1000, second_ms, &remainder);
    
    /* Calculate two decimal places */
    u32 decimal = div_u64_rem(remainder * 100, second_ms, &remainder);
    
    printk(KERN_INFO "speed >>> rate(times/s): %llu.%02u\n", 
           rate, decimal);
}

Key recommendations:

  • Prefer fixed-point arithmetic over floating point in kernel code.
  • Including floating-point calculations in kernel code is generally discouraged and should be avoided unless absolutely necessary.
  • Use the kernel’s math functions (div_s64, div_u64_rem, etc.) for better precision.
  • Consider whether you really need decimal precision in kernel logs
  • Logs in the kernel should focus on essential and easily interpretable data to reduce processing overhead and log size.
  • If possible, work with integer units (e.g., microseconds instead of fractional seconds).
1 Like

thanks for your professional explanation, I adopted your 3rd solution. With kernel’s interface div_u64_rem() I also simplified the Makefile by removing “EXTRA_CFLAGS += -msse -msse2”, which makes the makefile suitable for multiple embedded systems. it’s really awesome, thank you.

1 Like