Home > Net >  Comput modulo between floating point numbers in C
Comput modulo between floating point numbers in C

Time:01-30

I have the following code to compute modulo between two floating point numbers:

auto mod(float x, float denom)
{
    return x>= 0 ? std::fmod(x, denom) : denom   std::fmod(x   1.0f, denom) - 1.0f;
}

It does only work partially for negative x:

-8 0
-7.75 0.25
-7.5 0.5
-7.25 0.75
-7 1
-6.75 1.25
-6.5 1.5
-6.25 1.75
-6 2
-5.75 2.25
-5.5 2.5
-5.25 2.75
-5 3
-4.75 -0.75 <== should be 3.25
-4.5 -0.5   <== should be 3.5
-4.25 -0.25 <== should be 3.75
-4 0
-3.75 0.25
-3.5 0.5
-3.25 0.75
-3 1
-2.75 1.25
-2.5 1.5
-2.25 1.75
-2 2
-1.75 2.25
-1.5 2.5
-1.25 2.75
-1 3
-0.75 3.25
-0.5 3.5
-0.25 3.75
0 0

How to fix it for negative x. Denom is assumed to be an integer greater than 0. Note: fmod as is provided by the standard library is broken for x < 0.0f.

x is in the left column, and the output is in the right column, like so:

for(size_t k = 0; k != 65;   k)
{
    auto x = 0.25f*(static_cast<float>(k) - 32);
    printf("%.8g %.8g\n", x, mod(x, 4));
}

CodePudding user response:

Note: fmod as is provided by the standard library is broken for x < 0.0f

I guess you want the result to always be a positive value1:

In mathematics, the result of the modulo operation is an equivalence class, and any member of the class may be chosen as representative; however, the usual representative is the least positive residue, the smallest non-negative integer that belongs to that class (i.e., the remainder of the Euclidean division).

The usual workaround was shown in Igor Tadetnik's comment, but that seems not enough.

@IgorTandetnik That worked. Pesky signed zero though, but I guess you cannot do anything about that.

Well, consider this(2, 3):

auto mod(double x, double denom)
{
    auto const r{ std::fmod(x, denom) };
    return std::copysign(r < 0 ? r   denom : r, 1);
}

1) https://en.wikipedia.org/wiki/Modulo
2) https://en.cppreference.com/w/cpp/numeric/math/copysign
3) https://godbolt.org/z/fdr9cbsYT

  • Related