have problem of converting a double (say N) to p/q form
... when N = 8.2
A typical double
cannot encode 8.2 exactly. Instead the closest representable double
is about
8.19999999999999928945726423989981412887573...
8.20000000000000106581410364015027880668640... // next closest
When code does
double N = 8.2;
It will be the 8.19999999999999928945726423989981412887573...
that is converted into rational form.
Converting a double
to p/q form:
Multiply double N by a large number say $k = 10^{10}$
This may overflow the double
. First step should be to determine if the double
is large, it which case, it is a whole number.
Do not multiple by some power of 10 as double
certainly uses a binary encoding. Multiplication by 10, 100, etc. may introduce round-off error.
C implementations of double
overwhelmingly use a binary encoding, so that FLT_RADIX == 2
.
Then every finite double x
has a significand that is a fraction of some integer over some power of 2: a binary fraction of DBL_MANT_DIG
digits @Richard Critten. This is often 53 binary digits.
Determine the exponent of the double
. If large enough or x == 0.0
, the double
is a whole number.
Otherwise, scale a numerator and denominator by DBL_MANT_DIG
. While the numerator is even, halve both the numerator and denominator. As the denominator
is a power-of-2, no other prime values are needed for simplification consideration.
#include <float.h>
#include <math.h>
#include <stdio.h>
void form_ratio(double x) {
double numerator = x;
double denominator = 1.0;
if (isfinite(numerator) && x != 0.0) {
int expo;
frexp(numerator, &expo);
if (expo < DBL_MANT_DIG) {
expo = DBL_MANT_DIG - expo;
numerator = ldexp(numerator, expo);
denominator = ldexp(1.0, expo);
while (fmod(numerator, 2.0) == 0.0 && denominator > 1.0) {
numerator /= 2.0;
denominator /= 2.0;
}
}
}
int pre = DBL_DECIMAL_DIG;
printf("%.*g --> %.*g/%.*g\n", pre, x, pre, numerator, pre, denominator);
}
int main(void) {
form_ratio(123456789012.0);
form_ratio(42.0);
form_ratio(1.0 / 7);
form_ratio(867.5309);
}
Output
123456789012 --> 123456789012/1
42 --> 42/1
0.14285714285714285 --> 2573485501354569/18014398509481984
867.53089999999997 --> 3815441248019913/4398046511104