Reputation: 25
Whenever a user inputs a fraction, it might be contain decimals (long double). I have some code (C++) below to show how I implement this:
class Fraction
{
public:
long double numerator, denominator;
Fraction(long double _numerator, long double _denominator) : numerator(_numerator), denominator(_denominator) {}
}
bool isInt(long double a)
{
long double intPart;
std::modf(a, &intPart);
return (a == intPart);
}
void simplifyRealFraction(Fraction& frac)
{
constexpr long double MULTIPLE_FACTOR = 2L;
constexpr int EXPONENT_LIMIT = 100;
int n_exp, d_exp;
std::frexp(frac.numerator, &n_exp);
std::frexp(frac.denominator, &d_exp);
long double new_n = frac.numerator / std::exp2(std::max(n_exp,d_exp));
long double new_d = frac.denominator / std::exp2(std::max(n_exp,d_exp));
while(!isInt(new_n) || !isInt(new_d))
{
new_n *= MULTIPLE_FACTOR;
new_d *= MULTIPLE_FACTOR;
}
std::frexp(new_n, &n_exp);
std::frexp(new_d, &d_exp);
if(n_exp > EXPONENT_LIMIT || d_exp > EXPONENT_LIMIT)
{
return;
}
long long int int_gcd = std::gcd(static_cast<long long int>(new_n), static_cast<long long int>(new_d));
new_n /= int_gcd;
new_d /= int_gcd;
frac.numerator = new_n;
frac.denominator = new_d;
}
However, when I try to run my function with the fraction 0.15 / 0.20, it changes it to a much larger, but equivalent value: 5.40432e+15 / 7.20576e+15
After some debugging, I found that std::gcd returned 1. I am assuming that this is a rounding error occurring in the floating point number from 0.15 to something else (14.999999 or similar for example).
Is there a better way to do this process? How can I avoid the rounding errors?
Upvotes: -1
Views: 89
Reputation: 93688
What you're seeing is how floating point numbers work. They don't hold numbers in decimal like .15. They hold them in binary. So it can hold .5 (2^-1), .25 (2^-2), .125 (2^-3), and other powers of 2 (or combos thereof) exactly. Something like .15 is held as .125 + .015625 (2^-6)+... So any number that isn't exactly a sum of powers of 2 will not be held exactly. It will be estimated.
If you truly need to hold them exactly, you need to use a fixed point math library. Or if you know that all numbers are exactly 2 decimal points, hold them as integer hundreths instead of whole numbers.
Upvotes: 3