This error looks more mysterious than it is. GCC is pointing at %, calling it “binary,” and then listing two types in parentheses. Read that message literally: the operator has a left operand and a right operand, and at least one of them is not a type that % accepts.

Why the compiler rejects this expression

broken_remainder.cc
int main(void)
{
    float value = 11.0f;
    double divisor = 3.0;
    double result = value % divisor;
 
    return result == 0.0;
}

The `%` expression is invalid because its operands are `float` and `double`.

Reading the types before reading the error

  • 11.0f has type float because of the f suffix.

  • 3.0 has type double; a decimal floating literal is double unless a suffix changes it.

  • % is a binary operator because it consumes two operands, but the C language restricts both operands to integer types.

  • Changing the result variable to int would not help. The invalid operation occurs on the right side before assignment.

Project directorybash
cc -std=c17 -Wall -Wextra -Wpedantic broken_remainder.c -o broken_remainder
broken_remainder.c: In function ‘main’:
broken_remainder.c:5:27: error: invalid operands to binary % (have ‘float’ and ‘double’)
    5 |     double result = value % divisor;
      |                           ^

What this compile step tells you

  • -std=c17 selects the C17 language mode; the diagnostic is a language constraint, not a warning-level preference.

  • -Wall -Wextra -Wpedantic adds useful diagnostics, but removing those flags will not make an invalid % expression legal.

  • The caret points to the operator, while the parenthetical type list identifies the actual mismatch to investigate.

Choose the repair based on what the numbers mean

There are two honest fixes. Neither begins with a cast. First decide whether the program is modelling indivisible units—packets, array positions, students, bytes—or measured quantities such as distance, voltage, and elapsed time.

Whole counts belong in integer types

When fractions are impossible by definition, represent that fact in the type system. Then % expresses exactly the operation you intended.

integer_divisibility.cc
#include <stdio.h>
 
int main(void)
{
    int items = 11;
    int group_size = 3;
    int leftover = items % group_size;
 
    printf("Complete groups: %d\n", items / group_size);
    printf("Items left over: %d\n", leftover);
 
    return 0;
}

Use integer remainder when the domain consists of whole counts.

Why this version fits the domain

  • int communicates that partial items and fractional group sizes are not valid inputs.

  • items / group_size performs integer division and returns the number of complete groups.

  • items % group_size returns 2, the whole items that remain after forming three groups.

  • Production code must still verify group_size != 0 before using either / or %.

Measured values need a floating-point remainder function

When the fractional part carries meaning, preserve it. The C math library provides a matching remainder function for each standard floating type.

  • fmodf(float x, float y) returns float.

  • fmod(double x, double y) returns double.

  • fmodl(long double x, long double y) returns long double.

floating_remainder.cc
#include <math.h>
#include <stdio.h>
 
int main(void)
{
    double distance = 11.5;
    double lap_length = 3.0;
 
    if (lap_length == 0.0) {
        fprintf(stderr, "lap length must not be zero\n");
        return 1;
    }
 
    double remainder = fmod(distance, lap_length);
    printf("Distance after complete laps: %.2f\n", remainder);
 
    return 0;
}

Use `fmod` when the operands and the remainder may contain fractional values.

Small choices that keep the fix correct

  • <math.h> declares fmod; calling it without the correct declaration is not a valid shortcut.

  • Both arguments are double, matching the fmod signature and preserving the .5 in distance.

  • fmod(x, y) computes x - trunc(x / y) * y; its result has the sign of x and magnitude smaller than |y| when defined.

  • A zero divisor is a domain error for fmod, so the example rejects it before making the call.

Project directorybash
cc -std=c17 -Wall -Wextra -Wpedantic floating_remainder.c -lm -o floating_remainder
./floating_remainder
Distance after complete laps: 2.50

About the -lm at the end

  • -lm links the system math library on toolchains where fmod is not linked automatically.

  • Library order can matter with traditional Unix linkers, so place -lm after the source or object files that use it.

  • The executable prints 2.50 because three complete lap lengths account for 9.0 of the 11.5 distance.

Do not confuse fmod with remainder

The C library also has a function literally named remainder. It is not simply another spelling of fmod: it chooses its result using a quotient rounded to the nearest integer, while fmod uses a quotient truncated toward zero. For code being translated from %, fmod usually matches the intended remainder model more closely.

Floating-point “divisibility” needs a tolerance

Many decimal fractions cannot be represented exactly in binary floating point. As a result, testing fmod(value, step) == 0.0 can reject a value that is mathematically a multiple. A tolerance must reflect the scale and error budget of your application; there is no universal epsilon.

approximate_multiple.cc
#include <float.h>
#include <math.h>
#include <stdbool.h>
 
bool is_approximately_multiple(double value, double step)
{
    if (!isfinite(value) || !isfinite(step) || step <= 0.0) {
        return false;
    }
 
    double r = fmod(value, step);
    double scale = fmax(fabs(value), fabs(step));
    double tolerance = 8.0 * DBL_EPSILON * scale;
 
    return fabs(r) <= tolerance || fabs(step - fabs(r)) <= tolerance;
}

A scale-aware starting point for finite values and a positive, nonzero step.

What this helper does—and does not promise

  • isfinite rejects NaN and infinity, while step <= 0.0 establishes the helper’s positive-step contract.

  • DBL_EPSILON describes the spacing near 1.0; scaling it makes the initial tolerance respond to operand magnitude.

  • The second comparison handles a remainder that lands just below step because of rounding near a multiple.

  • The factor 8.0 is an engineering choice, not a language rule. Replace it with a tolerance justified by measurement precision and accumulated computation error.

A quick diagnostic checklist

  • `float` and `double`: use fmodf or fmod; do not change only the destination type.

  • Unexpected `double` operand: inspect decimal literals—3.0 is double, whereas 3.0f is float.

  • Undefined reference to `fmod`: add -lm after the object files on platforms that require explicit math-library linkage.

  • Wrong printed value: use %f, %g, or another floating conversion for a floating result; %d expects an int.

  • No fractional values should exist: redesign the variables as integer types instead of repeatedly casting them at the operator.

  • Comparing with zero: decide whether exact binary equality is truly intended or whether the domain needs a documented tolerance.

Continue with the right remainder topic

For a deeper comparison of fmod, fmodf, fmodl, and remainder, continue with floating-point remainder in C. If your operands are integers and the real goal is an exact divisibility test, use the C modulo and divisibility example.

Technical references