Wednesday, April 6, 2022

[SOLVED] GCC mod() definition

Issue

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.0f * ( 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)

Sollya and Octave defines as:

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?


Solution

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 operation x/y calculated by this function is exactly the value x - n*y, where the value n is the integral value nearest the exact value x/y. When |n-x/y| = ½, the value n 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

godbolt example


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)



Answered By - Turtlefight
Answer Checked By - Cary Denson (WPSolving Admin)