Mathematical Rounding and Remainder Functions in C

Key Takeaways
  • The C standard mathematical library provides several functions to round floating-point numbers according to specific rules: ceil, floor, trunc, round, nearbyint and rint.
  • The rounding functions can return the result as a floating-point value or as an integer value: lround and llround.
  • The C standard mathematical library also provides functions to calculate the remainder of division between floating-point numbers: fmod, remainder and remquo.

Rounding Functions

The C standard mathematical library provides several functions to approximate floating-point numbers.

All these functions are declared in the <math.h> header.

We can divide these functions into three categories:

  • Functions that round a floating-point number upward or downward and return it as a floating-point value:

    Function Description Notes
    double ceil(double x); Returns the smallest integer greater than or equal to x.
    double ceilf(float x); float version of ceil. C99
    double ceill(long double x); long double version of ceil. C99
    double floor(double x); Returns the largest integer less than or equal to x.
    double floorf(float x); float version of floor. C99
    double floorl(long double x); long double version of floor. C99
    Table 1: Ceil and floor functions of the standard mathematical library.

    The ceil function and its variants return the smallest integer (in the form of double) that is greater than or equal to the value passed as an argument. Indeed, the name "ceil" derives from the English word "ceiling", which means "ceiling" or "upper part".

    Conversely, the floor function and its variants return the largest integer (in the form of double) that is less than or equal to the value passed as an argument. The name "floor" derives from the English word "floor", which means "floor" or "lower part".

    Mathematically, we can express the behavior of these functions as follows:

    \left\lceil x \right\rceil = \min \{ n \in \mathbb{Z} \mid n \geq x \}
    \left\lfloor x \right\rfloor = \max \{ n \in \mathbb{Z} \mid n \leq x \}

    For example:

    • ceil(4.2) returns 5.0
    • ceil(-4.2) returns -4.0
    • floor(4.2) returns 4.0
    • floor(-4.2) returns -5.0

    In other words, the ceil function always rounds upward, while the floor function always rounds downward.

  • Functions that round a floating-point number always toward zero and return it as a floating-point value:

    Function Description Notes
    double trunc(double x); Returns the integer part of x, rounded toward zero. C99
    double truncf(float x); float version of trunc. C99
    double truncl(long double x); long double version of trunc. C99
    Table 2: Trunc functions of the standard mathematical library.

    The trunc function and its variants return the integer part of the number passed as an argument, always rounded toward zero. For example:

    • trunc(4.7) returns 4.0
    • trunc(-4.7) returns -4.0

    In other words, the trunc function eliminates the decimal part of the number regardless of the sign of the number itself. Therefore, trunc always rounds toward zero.

    Indeed, we can express the behavior of this function as follows:

    \text{trunc}(x) = \begin{cases} \left\lfloor x \right\rfloor &amp; \text{if } x \geq 0 \\ \left\lceil x \right\rceil &amp; \text{if } x &lt; 0 \end{cases}
  • Functions that round a floating-point number away from zero and return it as a floating-point value:

    Function Description Notes
    double round(double x); Returns the value of x rounded to the nearest integer. In case of tie, rounds away from zero. C99
    double roundf(float x); float version of round. C99
    double roundl(long double x); long double version of round. C99
    Table 3: Round functions of the standard mathematical library.

    The round function and its variants round the number passed as an argument to the nearest integer. In case of tie (when the decimal part is exactly 0.5), the function rounds away from zero. For example:

    • round(4.5) returns 5.0
    • round(4.4) returns 4.0
    • round(-4.5) returns -5.0
    • round(-4.4) returns -4.0

    In other words, the round function follows the standard rounding rule, but in case of tie, it ensures that the result is always farther from zero.

    Indeed, we can express the behavior of this function as follows:

    \text{round}(x) = \begin{cases} \left\lfloor x + 0.5 \right\rfloor &amp; \text{if } x \geq 0 \\ \left\lceil x - 0.5 \right\rceil &amp; \text{if } x &lt; 0 \end{cases}

In addition to these functions that round according to specific rules, the library also provides functions to round a number according to the current rounding mode defined in the mathematical execution environment:

Function Description Notes
double nearbyint(double x); Returns the value of x rounded according to the current rounding mode. Does not raise overflow exceptions. C99
double nearbyintf(float x); float version of nearbyint. C99
double nearbyintl(long double x); long double version of nearbyint. C99
double rint(double x); Returns the value of x rounded according to the current rounding mode. May raise inexact or overflow exceptions. C99
double rintf(float x); float version of rint. C99
double rintl(long double x); long double version of rint. C99
Table 4: Nearbyint and rint functions of the standard mathematical library.

These functions round the number passed as an argument according to the current rounding mode defined in the mathematical execution environment. The main difference between the two is that nearbyint does not raise overflow exceptions, while rint may raise inexact or overflow exceptions.

In practice, depending on the current rounding mode, these functions can behave like ceil, floor, trunc or round.

Integer Rounding Functions

The functions seen above round floating-point numbers to an integer according to specific rules, but still return a floating-point value.

The C standard mathematical library also provides functions that round floating-point numbers to an integer and return the result as an integer value.

Function Description Notes
long int lround(double x); Returns the value of x rounded to the nearest integer as long int. In case of tie, rounds away from zero. C99
long int lroundf(float x); float version of lround. C99
long int lroundl(long double x); long double version of lround. C99
long int llround(double x); Returns the value of x rounded to the nearest integer as long long int. In case of tie, rounds away from zero. C99
long int llroundf(float x); float version of llround. C99
long int llroundl(long double x); long double version of llround. C99
Table 5: Lround and llround functions of the standard mathematical library.

These functions behave similarly to the round function seen previously, rounding the number passed as an argument to the nearest integer. In case of tie, they round away from zero.

The difference, however, is that these functions return the result as an integer value (long int or long long int) instead of as a floating-point value.

For example:

  • lround(4.5) returns 5
  • lround(4.4) returns 4
  • llround(-4.5) returns -5
  • llround(-4.4) returns -4

Remainder Function

In C language, there exists the modulo operator (or remainder operator) % which calculates the remainder of division between two integers.

However, it is not possible to apply the % operator directly to floating-point numbers. For this reason, the C standard mathematical library provides specific functions to calculate the remainder of division between two floating-point numbers.

Function Description Notes
double fmod(double x, double y); Returns the remainder of the division of x by y.
double fmodf(float x, float y); float version of fmod. C99
double fmodl(long double x, long double y); long double version of fmod. C99
double remainder(double x, double y); Returns the remainder of the division of x by y, where the quotient has been rounded to the nearest integer. C99
double remainderf(float x, float y); float version of remainder. C99
double remainderl(long double x, long double y); long double version of remainder. C99
double remquo(double x, double y, int *quo); Identical to remainder but also saves the quotient in *quo. C99
double remquof(float x, float y, int *quo); float version of remquo. C99
double remquol(long double x, long double y, int *quo); long double version of remquo. C99
Table 6: Fmod, remainder and remquo functions of the standard mathematical library.

The functions of the fmod family calculate the remainder of the division of x by y according to the standard mathematical definition of modulo. For example, fmod(5.3, 2.0) returns 1.3, since 5.3 = 2.0 * 2 + 1.3.

The functions of the remainder family calculate the remainder of the division of x by y in a particular way. To understand how it works, let's define n as the quotient of x divided by y, rounded to the nearest integer. The remainder function then returns the value:

r = x - n \cdot y

For example, remainder(5.3, 2.0) returns -0.7, since the rounded quotient is 3 (the nearest integer to 5.3 / 2.0 = 2.65), and therefore 5.3 - 3 * 2.0 = -0.7. If we had used fmod(5.3, 2.0), we would have obtained 1.3.

The remquo function is similar to remainder, but additionally saves the rounded quotient in an integer passed as the third argument. This can be useful if you want to know both the remainder and the quotient of the division.

For example:

#include <stdio.h>
#include <math.h>

int main() {
    double x = 5.3;
    double y = 2.0;
    int quo;

    double r1 = fmod(x, y);
    double r2 = remainder(x, y);
    double r3 = remquo(x, y, &quo);

    printf("fmod(%.1f, %.1f) = %.1f\n", x, y, r1);
    printf("remainder(%.1f, %.1f) = %.1f\n", x, y, r2);
    printf("remquo(%.1f, %.1f) = %.1f, quo = %d\n", x, y, r3, quo);

    return 0;
}

The output will be:

fmod(5.3, 2.0) = 1.3
remainder(5.3, 2.0) = -0.7
remquo(5.3, 2.0) = -0.7, quo = 3