In C programming, using the modulo operator % works seamlessly for integer types (int, long, short). However, attempting to calculate the remainder of two floating-point numbers (float or double) using % results in a compilation error: invalid operands to binary %.

To compute the floating-point remainder of division in C, standard C library provides fmod(), fmodf(), and fmodl() in <math.h>. In this article, we explain the mathematics behind fmod(), compare it with remainder(), and demonstrate compiler usage with GCC.

Quick Code Reference: fmod() Syntax

Include <math.h> and link the math library with -lm during GCC compilation:

quick_reference.cc
#include <stdio.h>
#include <math.h>
 
int main(void) {
    double numerator = 18.5;
    double denominator = 4.2;
 
    // Calculate floating-point remainder: fmod(18.5, 4.2)
    double remainder = fmod(numerator, denominator);
 
    printf("Remainder of %.2f / %.2f = %.2f
", numerator, denominator, remainder);
    // Output: Remainder of 18.50 / 4.20 = 1.70
    return 0;
}
GCC Compilation Commandbash
# Compile C program linking math library (-lm)
gcc -O2 quick_reference.c -lm -o quick_reference
./quick_reference

1. Why Modulo % Fails for Floating-Point Numbers

The % modulo operator is strictly defined for integer operands in the ISO C standard (C99/C11/C17). If you write 5.5 % 2.1, the GCC compiler rejects it immediately:

invalid_modulo.cc
#include <stdio.h>
 
int main(void) {
    float a = 9.7f;
    float b = 3.2f;
 
    // COMPILER ERROR: invalid operands to binary % (have 'float' and 'float')
    // float rem = a % b; 
 
    return 0;
}

2. Complete C fmod() Program with Precision Variants (main.c)

This program demonstrates remainder calculations across 32-bit float, 64-bit double, and 80/128-bit long double types while checking for division by zero (NaN):

main.cc
#include <stdio.h>
#include <math.h>
#include <errno.h>
 
int main(void) {
    printf("====================================================
");
    printf("    C FLOATING-POINT REMAINDER DEMONSTRATION         
");
    printf("====================================================
 
");
 
    // 1. Double Precision (64-bit fmod)
    double x1 = 25.75, y1 = 5.2;
    double rem1 = fmod(x1, y1);
    printf("[double]      fmod(%.2f, %.2f) = %.4f
", x1, y1, rem1);
    // Math logic: 25.75 - (4 * 5.2) = 25.75 - 20.8 = 4.95
 
    // 2. Single Precision (32-bit fmodf)
    float x2 = 14.8f, y2 = 3.5f;
    float rem2 = fmodf(x2, y2);
    printf("[float]       fmodf(%.2ff, %.2ff) = %.4f
", x2, y2, rem2);
    // Math logic: 14.8 - (4 * 3.5) = 14.8 - 14.0 = 0.8
 
    // 3. Extended Precision (long double fmodl)
    long double x3 = 123.456L, y3 = 10.5L;
    long double rem3 = fmodl(x3, y3);
    printf("[long double] fmodl(%.3Lf, %.3Lf) = %.4Lf
", x3, y3, rem3);
 
    // 4. Negative Numerator Handling
    double x4 = -18.5, y4 = 4.2;
    double rem4 = fmod(x4, y4);
    printf("[negative]    fmod(%.2f, %.2f) = %.2f
 
", x4, y4, rem4);
 
    // 5. Handling Edge Cases: Division by Zero
    double zero_divisor = 0.0;
    errno = 0;
    double invalid_rem = fmod(10.0, zero_divisor);
 
    if (isnan(invalid_rem)) {
        printf("Division by zero result: NaN (Not a Number)
");
    }
    if (errno == EDOM) {
        printf("Domain Error (EDOM) set by math library.
");
    }
 
    return 0;
}

fmod() vs remainder(): Mathematical Difference

  • `fmod(x, y)` (Truncated Division): Calculates x - n * y where n is trunc(x / y) (quotient truncated toward zero). The sign of the result matches the sign of x.

  • `remainder(x, y)` (Rounded Division - IEEE 754): Calculates x - n * y where n is rint(x / y) (quotient rounded to the nearest integer). If x / y is exactly half, n rounds to the nearest even integer.