I was making faster mod(x,2) function in C with GCC (compiled using -O3 -ffast-math) and bumped to difference in results between GCC and Octave:
float fast_fmod2(float x){ // over 50x faster than std::fmod(x, 2.0f)
x *= 0.5f;
return 2.0 * ( x - std::floor(x));
}
Result (mod(input,2.0f)):
Input : -7.8539786
std::fmod() : -1.853978633881
Octave mod(): 0.146021366119
fast_fmod2 : 0.146021366119
...
Input : 7.8539805
std::fmod() : 1.853980541229
Octave mod(): 1.853980541229
fast_fmod2 : 1.853980541229
I checked couple other math software as well and it looks like at least Sollya and Wolfram|Alpha supports Octave results and before mentioned documented same definition for the function as Octave did.
GCC defines mod function as:
mod(A, P) = A - (int(A/P) * P)
mod(a, b) = a - (b * floor(a / b))
Because of int(a/b) rounds differently compared to floor(a/b), GCC definition gives different answer for negative A's.
>> int16(-2.19/2)
ans = -1
>> floor(-2.19/2)
ans = -2
Is it a bug in GCC version or something else behind the difference?
CodePudding user response:
I'm assuming you mean std:fmod
instead of std::mod
(there's no std::mod
in the official c standard)
The reason for this difference is that std::fmod
doesn't do what you think it does.
std::fmod
calculates the remainder and not the arithmetic modulus.
Computes the floating-point remainder of the division operation
x
/y
If you want the arithmetic modulus you need to use std::remainder
instead:
Computes the IEEE remainder of the floating point division operation
x
/y
. The IEEE floating-point remainder of the division operationx/y
calculated by this function is exactly the valuex - n*y
, where the valuen
is the integral value nearest the exact valuex/y
. When|n-x/y| = ½
, the valuen
is chosen to be even.
This will produce the expected result in your example:
std::cout << std::remainder(-7.8539786f, 2.0f) << std::endl; // 0.146021
So to answer your question: This is not a bug, this is intended behaviour.
The functions are just named differently in C (albeit with a slighly confusing naming scheme)
- Octave
mod
== Cstd::reminder
- Octave
rem
== Cstd::fmod