(Too long for a comment.)
Some comments claim that this is not possible. But I am of a contrary opinion.
I am of the opinion that it is possible in the right interpretation, but it is too easy to misstate the question or misunderstand the answer.
The question posed here is to find rational approximation(s) to a given floating point value.
This is certainly possible since floating point formats used in C++ can only store rational values, most often in the form of sign/mantissa/exponent. Taking IEEE-754 single precision format as an example (to keep the numbers simpler), 0.333
is stored as 1499698695241728 * 2^(-52)
. That is equivalent to the fraction 1499698695241728 / 2^52
whose convergents provide increasingly accurate approximations, all the way up to the original value: 1/3
, 333/1000
, 77590/233003
, 5586813/16777216
.
Two points of note here.
For a variable float x = 0.333;
the best rational approximation is not necessarily 333 / 1000
, since the stored value is not exactly 0.333
but rather 0.333000004291534423828125
because of the limited precision of the internal representation of floating points.
Once assigned, a floating point value has no memory of where it came from, or whether the source code had it defined as float x = 0.333;
vs. float x = 0.333000004;
because both of those values have the same internal representation. This is why the (related, but different) problem of separating a string representation of a number (for example, a user-entered value) into integer and fractional parts cannot be solved by first converting to floating point then running floating point calculations on the converted value.
[ EDIT ] Following is the step-by-step detail of the 0.333f
example.
- The code to convert a
float
to an exact fraction.
#include <cfloat>
#include <cmath>
#include <limits>
#include <iostream>
#include <iomanip>
void flo2frac(float val, unsigned long long* num, unsigned long long* den, int* pwr)
{
float mul = std::powf(FLT_RADIX, FLT_MANT_DIG);
*den = (unsigned long long)mul;
*num = (unsigned long long)(std::frexp(val, pwr) * mul);
pwr -= FLT_MANT_DIG;
}
void cout_flo2frac(float val)
{
unsigned long long num, den; int pwr;
flo2frac(val, &num, &den, &pwr);
std::cout.precision(std::numeric_limits<float>::max_digits10);
std::cout << val << " = " << num << " / " << den << " * " << FLT_RADIX << "^(" << pwr << ")" << std::endl;
}
int main()
{
cout_flo2frac(0.333f);
}
- Output.
0.333000004 = 11173626 / 16777216 * 2^(-1)
This gives the rational representation of float val = 0.333f;
as 5586813/16777216
.
What remains to be done is determine the convergents of the exact fraction, which can be done using integer calculations, only. The end result is (courtesy WA):
0, 1/3, 333/1000, 77590/233003, 5586813/16777216