简体   繁体   中英

Comput modulo between floating point numbers in C++

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));
}

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 value 1 :

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 (ie, 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM