views:

1891

answers:

8

Is there a library that will convert a Double to a String with the whole number, followed by a fraction?

For example

1.125 = 1 1/8

I am only looking for fractions to a 64th of an inch.

+5  A: 

One problem you might run into is that not all fractional values can be represented by doubles. Even some values that look simple, like 0.1. Now on with the pseudocode algorithm. You would probably be best off determining the number of 64ths of an inch, but dividing the decimal portion by 0.015625. After that, you can reduce your fraction to the lowest common denominator. However, since you state inches, you may not want to use the smallest common denominator, but rather only values for which inches are usually represented, 2,4,8,16,32,64.

One thing to point out however, is that since you are using inches, if the values are all proper fractions of an inch, with a denominator of 2,4,8,16,32,64 then the value should never contain floating point errors, because the denominator is always a power of 2. However if your dataset had a value of .1 inch in there, then you would start to run into problems.

Kibbee
The data coming in is good (always in 64ths of an inch)
Milhous
+3  A: 

I don't necessarily agree, base on the fact that Milhous wants to cover inches up to 1/64" Suppose that the program demands 1/64" precision at all times, that should take up 6 bits of the mantissa. In a float, there's 24-6 = 18, which (if my math is right), should mean that he's got a range of +/- 262144 + 63/64"

That might be enough precision in the float to convert properly into the faction without loss.

And since most people working on inches uses denominator of powers of 2, it should be fine.

But back to the original question, I don't know any libraries that would do that.

Calyth
+1  A: 

Function for this in a C-variant called LPC follows. Some notes:

  1. Addition to input value at beginning is to try to cope with precision issues that otherwise love to wind up telling you that 5 is 4 999999/1000000.
  2. The to_int() function truncates to integer.
  3. Language has a to_string() that will turn some floats into exponential notation.


string strfrac(float frac) {
    int main = to_int(frac + frac / 1000000.0);
    string out = to_string(main);
    float rem = frac - to_float(main);
    string rep;
    if(rem > 0 && (to_int(rep = to_string(rem)) || member(rep, 'e') == Null)) {
        int array primes = ({ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 });
        string base;
        int exp;
        int num;
        int div;
        if(sscanf(rep, "%se%d", base, exp) == 2) {
            num = to_int(replace(base, ".", ""));
            div = to_int(pow(10, abs(exp)));
        } else {
            rep = rep[2..];
            num = to_int(rep);
            div = to_int(pow(10, strlen(rep)));
        }
        foreach(int prime : primes) {
            if(prime > num)
                break;
            while((num / prime) * prime == num && (div / prime) * prime == div) {
                num /= prime;
                div /= prime;
            }
        }
        out += " " + num + "/" + div;
    }
    return out;
}
chaos
+7  A: 

Your problem is pretty simple, because you're assured the denominator will always divide 64. in C# (someone feel free to translate a Java version):

string ToMixedFraction(decimal x) 
{
    int whole = (int) x;
    int denominator = 64;
    int numerator = (int)( (x - whole) * denominator );

    if (numerator == 0) 
    {
        return whole.ToString();
    }
    while ( numerator % 2 == 0 ) // simplify fraction
    {
        numerator /= 2;
        denominator /=2;
    }
    return string.Format("{0} {1}/{2}", whole, numerator, denominator);
}

Bonus: Code Golf

public static string ToMixedFraction(decimal x) {
    int w = (int)x,
        n = (int)(x * 64) % 64,
        a = n & -n;
    return w + (n == 0 ? "" : " " + n / a + "/" + 64 / a);
}
Jimmy
Note that checking the denominator for even-ness is unnecessary. See my Java version.
erickson
egads, you're right :)
Jimmy
Now look what you made me do... once you start trimming down code you just can't stop.
Jimmy
A: 

My code looks like this.

public static int gcd(int a, int b)
 {
  if (b == 0)
   return a;
  else
   return gcd(b, a % b);
 }

public static String doubleToStringFraction(Double d)
 {
  StringBuffer result = new StringBuffer(" " + ((int) Math.floor(d)));
  int whole = (int) ((d - Math.floor(d)) * 10000);
  int gcd = gcd(whole, 10000);
  result.append(" " + (whole / gcd) + "/" + 10000 / gcd + " ");
  return result.toString();
 }
Milhous
A: 

To solve this problem (in one of my projects), I took the following steps:

  • Built a dictionary of decimal/fraction strings.
  • Wrote a function to search the dictionary for the closest matching fraction depending on the "decimal" part of the number and the matching criteria.
+4  A: 

How about org.apache.commons.math ? They have a Fraction class that takes a double.

http://commons.apache.org/math/api-1.2/org/apache/commons/math/fraction/Fraction.html

You should be able to extend it and give it functionality for the 64th. And you can also add a toString that will easily print out the whole number part of the fraction for you.

Fraction(double value, int maxDenominator) Create a fraction given the double value and maximum denominator.

Mongo
I give it a look, thanks.
Milhous
A: 

As several others have poited out, fractions of 64 can be precicely represented by IEEE-floats. This means we can also convert to a fraction by moving and masking bits.

This is not the place to explain all details of floating point representations, please refer to wikipedia for details.

Briefly: a floating point number is stored as (sign)(exp)(frac) where sign is 1 bit, exp is 11 bits and frac is the fraction part (after 1.) and is 52 bits. This is enterpreted as the number:

(sign == 1 ? -1 : 1) * 1.(frac) * 2^(exp-1023)

Thus, we can get the 64th by moving the point accoring to the exponent and masking out the 6 bits after the point. In Java:

private static final long MANTISSA_FRAC_BITMAP = 0xfffffffffffffl;
private static final long MANTISSA_IMPLICIT_PREFIX = 0x10000000000000l;
private static final long DENOM_BITMAP = 0x3f; // 1/64
private static final long DENOM_LEN = 6;
private static final int FRAC_LEN = 52;

public String floatAsFrac64(double d) {
 long bitmap = Double.doubleToLongBits(d);
 long mantissa = bitmap & MANTISSA_FRAC_BITMAP | MANTISSA_IMPLICIT_PREFIX;
 long exponent = ((bitmap >> FRAC_LEN) & 0x7ff) - 1023;
 boolean negative = (bitmap & (1l << 63)) > 0;

 // algorithm:
 //   d is stored as SE(11)F(52), implicit "1." before F
 //   move point to the right <exponent> bits to the right:
 if(exponent > FRAC_LEN) System.out.println("warning: loosing precision, too high exponent");
 int pointPlace = FRAC_LEN-(int)exponent;
 //   get the whole part as the number left of the point: 
 long whole = mantissa >> pointPlace;
 //   get the frac part as the 6 first bits right of the point: 
 long frac = (mantissa >> (pointPlace-DENOM_LEN)) & DENOM_BITMAP;
 //   if the last operation shifted 1s out to the right, we lost precision, check with
 //   if any of these bits are set:
 if((mantissa & ((MANTISSA_FRAC_BITMAP | MANTISSA_IMPLICIT_PREFIX) >> (pointPlace - DENOM_LEN))) > 0) {
  System.out.println("warning: precision of input is smaller than 1/64");
 }
 if(frac == 0) return String.format("%d", whole);
 int denom = 64;
 // test last bit, divide nom and demon by 1 if not 1
 while((frac & 1) == 0) {
  frac = frac >> 1;
  denom = denom >> 1;
 }
 return String.format("%d %d/%d", whole, frac, denom);
}

(this code can probably be made shorter, but reading bit-flipping-code like this is hard enough as it is...)

Rolf Rander