2

Possible Duplicate:
How can I turn a floating point number into the closest fraction represented by a byte numerator and denominator?

I would like to take an arbitrary float or double in Java and convert it to a rational number - ie. a number of the form a/b where a and b are long integers. How can I do this in a reasonably efficient way?

(BTW - I already have code for simplifying the fractions, so it doesn't matter whether a/b are in their simplest form).

Community
  • 1
  • 1
sanity
  • 35,347
  • 40
  • 135
  • 226
  • What about the case that you are passed an irrational number? i.e., a number that cannot be represented as `a/b` – Chris Dargis Nov 04 '12 at 20:41
  • 6
    @Doug Ramsey - Computers don't really have irrational numbers. If you're given a float or a double, it's rational. – Don Roby Nov 04 '12 at 20:43
  • I am "fairly certain" there are existing libraries to do this. Also, it shouldn't be too hard to find a language-agnostic (or implementation in another language) algorithm for the process. –  Nov 04 '12 at 20:52
  • 2
    My answer here gives the simplest form to the precision you define http://stackoverflow.com/questions/5968636/converting-a-float-into-a-string-fraction-representation/5968920#5968920 It the example it converts PI to different levels of precision. – Peter Lawrey Nov 04 '12 at 21:03
  • Peter, interesting - but is that really efficient? Seems like it wouldn't be. – sanity Nov 04 '12 at 21:11
  • This question has been asked on Stack Overflow numerous times. There is a continued fraction algorithm that is the most well known way, although the duplicate I proposed above suggests an algorithm using a Stern–Brocot tree. (I am assuming the intent is that the floating-point number approximates a fraction, such as 5/7, and it is desired to recover that fraction. This is different from returning the exact floating-point number in a fraction form.) – Eric Postpischil Nov 05 '12 at 00:15
  • The question mentions "arbitrary float or double". Only some floats and doubles can be represented as the ratio of two longs. For example, 2**100 can be exactly represented as a double, but not as the ratio of two longs. Is the input a limited range of float or double? If not, consider BigInteger instead of long. – Patricia Shanahan Nov 05 '12 at 02:48

5 Answers5

5

First see how double (or float but I refer only to double below) is constructed by IEEE-754 rules:

Then convert double to bits with Double.doubleToLongBits. Count the fraction using this method 1 + bit_0 * 2^(-1) + bit_1 * 2^(-2) .... Multiply the result with exponent (2^(exponent) to be precise) and with a sign.

Here is the code:

double number =  -0.15625;
// Code below doesn't work for 0 and NaN - just check before

long bits = Double.doubleToLongBits(number);

long sign = bits >>> 63;
long exponent = ((bits >>> 52) ^ (sign << 11)) - 1023;
long fraction = bits << 12; // bits are "reversed" but that's not a problem

long a = 1L;
long b = 1L;

for (int i = 63; i >= 12; i--) {
    a = a * 2 + ((fraction >>> i) & 1);
    b *= 2;
}

if (exponent > 0)
    a *= 1 << exponent;
else
    b *= 1 << -exponent;

if (sign == 1)
    a *= -1;

// Here you have to simplify the fraction

System.out.println(a + "/" + b);

But be careful - with big exponents you may run into numbers that won't fit into your variables. In fact you may consider storing exponent along the fraction and only multiply it if exponent is small enough. If it's not and you have to display the fraction to the user you may use the scientific notation (that requires solving equation 2^n = x * 10^m where m is your decimal exponent and x is a number you have to multiply the fraction with. But that's a matter for another question...).

zduny
  • 2,481
  • 1
  • 27
  • 49
  • Thanks, although I was hoping for a more detailed answer that might describe an efficient way to do the conversion. – sanity Nov 04 '12 at 21:10
  • That process doesn't give you a rational number. It only gives you the value. Not an answer. – user207421 Nov 04 '12 at 21:27
  • @EJP With this method you have sequence `1 + bit * 1/2 + bit * 1/4 ...`. Its not hard to figure out how to make a fraction from it... – zduny Nov 04 '12 at 21:31
  • There is no method here, only essentially the definition of the mantissa. What you have left to be 'figured out' is the answer to the question. – user207421 Nov 04 '12 at 21:38
  • Well, I assumed everyone can add fractions... anyway I will edit the answer to provide exact code. – zduny Nov 05 '12 at 08:16
  • It works upto a certain extent. When I try to approximate `0.0003`, I get `5534023222112865/0`, which is... er... wrong. – Hungry Blue Dev Apr 01 '16 at 12:32
2

Let long bits = Double.doubleToLongBits(double). From the Javadoc of Double.longBitsToDouble:

...let s, e, and m be three values that can be computed from the argument:

int s = ((bits >> 63) == 0) ? 1 : -1;
int e = (int)((bits >> 52) & 0x7ffL);
long m = (e == 0) ?
             (bits & 0xfffffffffffffL) << 1 :
             (bits & 0xfffffffffffffL) | 0x10000000000000L;

Then the floating-point result equals the value of the mathematical expression s·m·2e-1075.

That result is most certainly a rational number.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
1

The rational number corresponding to any FP value is (mantissa/2^-exponent), where the mantissa and exponent are as defined in IEEE 754 (Wiki reference). You can then apply divisions by LCD (or I guess GCF) to get a canonical rational number.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • 1
    Are you aware of an easy way to obtain the mantissa and exponent for a given float or double? – sanity Nov 04 '12 at 21:40
  • Nice relation, but .. missing the details of how to implement it (in Java). –  Nov 04 '12 at 22:23
1

The various concepts contained under the rubric continued fractions yield best-possible rational approximations for a given maximum denominator. Specifically, you're asking about calculating a convergent sequence. At some point, when your denominator is large enough according to whatever criteria you want (or are forced upon you by finite integer implementation lengths), terminate calculating the convergent terms and use the last one. Algorithms are described in rather good detail on the linked Wikipedia pages.

To address one concern you raised, the fractions generated in the convergent sequence are always in reduced form. They are also provably the best possible approximations for a given denominator. Precisely, a convergent term of the form m/n is closer to the target number than another other fraction with denominator < n. In other words, the convergent algorithm yields better approximations than trial and error.

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
eh9
  • 7,340
  • 20
  • 43
0

As you know, floating point numbers cannot store even simple numbers such as 0.1 exactly. If you use a naïve approach for converting floating point numbers, then you might end up with huge numerators and denomiators.

However, there are algorithms that might help: the Dragon4 and Grisu3 algorithms are designed to create the most readable output for floating point numbers. They take into advantage that certain floating point bit sequences can be expressed by several decimal fractions and choose the shortest of these.

For a first implementation, I would use Dragon4 and/or Grisu3 to create the shortest decimal fraction out of the floating point. For example the floating point number with the bits cd cc cc cc cc cc f4 3f would result in the decimal fraction 1.3 instead of 1.29999999. I then would express the decimal fraction in the form a/b and simplify it. In the given example, this would be 13/10, with no further simplification.

Please note that the conversion into a decimal fraction may be a disadvantage. For example, the rational number 1/3 cannot be expressed exactly in both decimal and floating point numbers. So, the best solution would be to modify an algorithm such as Dragon4 to use arbitrary fractional denominators and not just 10. Alas, this almost certainly will require quite a lot of work and some CS background.

nd.
  • 8,699
  • 2
  • 32
  • 42