PHP convert decimal into fraction and back?

30,934

Solution 1

I think I'd store the string representation too, as, once you run the math, you're not getting it back!

And, here's a quick-n-dirty compute function, no guarantees:

$input = '1 1/2';
$fraction = array('whole' => 0);
preg_match('/^((?P<whole>\d+)(?=\s))?(\s*)?(?P<numerator>\d+)\/(?P<denominator>\d+)$/', $input, $fraction);
$result = $fraction['whole'] + $fraction['numerator']/$fraction['denominator'];
print_r($result);die;

Oh, for completeness, add a check to make sure $fraction['denominator'] != 0.

Solution 2

Sometimes you need to find a way to do it and rounding is acceptable. So if you decide what range of rounding works out for you you can build a function like this. To convert a decimal into the fraction that it most closely matches. You can extend the accuracy by adding more denominators to be tested.

function decToFraction($float) {
    // 1/2, 1/4, 1/8, 1/16, 1/3 ,2/3, 3/4, 3/8, 5/8, 7/8, 3/16, 5/16, 7/16,
    // 9/16, 11/16, 13/16, 15/16
    $whole = floor ( $float );
    $decimal = $float - $whole;
    $leastCommonDenom = 48; // 16 * 3;
    $denominators = array (2, 3, 4, 8, 16, 24, 48 );
    $roundedDecimal = round ( $decimal * $leastCommonDenom ) / $leastCommonDenom;
    if ($roundedDecimal == 0)
        return $whole;
    if ($roundedDecimal == 1)
        return $whole + 1;
    foreach ( $denominators as $d ) {
        if ($roundedDecimal * $d == floor ( $roundedDecimal * $d )) {
            $denom = $d;
            break;
        }
    }
    return ($whole == 0 ? '' : $whole) . " " . ($roundedDecimal * $denom) . "/" . $denom;
}

Solution 3

To can use PEAR's Math_Fraction class for some of your needs

<?php

include "Math/Fraction.php";

$fr = new Math_Fraction(1,2);


// print as a string
// output: 1/2
echo $fr->toString();

// print as float
// output: 0.5
echo $fr->toFloat();

?>

Solution 4

Here is a solution that first determines a valid fraction (although not necessarily the simplest fraction). So 0.05 -> 5/100. It then determines the greatest common divisor of the numerator and denominator to reduce it down to the simplest fraction, 1/20.

function decimal_to_fraction($fraction) {
  $base = floor($fraction);
  $fraction -= $base;
  if( $fraction == 0 ) return $base;
  list($ignore, $numerator) = preg_split('/\./', $fraction, 2);
  $denominator = pow(10, strlen($numerator));
  $gcd = gcd($numerator, $denominator);
  $fraction = ($numerator / $gcd) . '/' . ($denominator / $gcd);
  if( $base > 0 ) {
    return $base . ' ' . $fraction;
  } else {
    return $fraction;
  }
}

# Borrowed from: http://www.php.net/manual/en/function.gmp-gcd.php#69189
function gcd($a,$b) {
  return ($a % $b) ? gcd($b,$a % $b) : $b;
}

This includes a pure PHP implementation of the gcd although if you are sure the gmp module is installed you could use the one that comes with gcd.

As many others have noted you need to use rational numbers. So if you convert 1/7 to a decimal then try to convert it back to a decimal you will be out of luck because the precision lost will prevent it from getting back to 1/7. For my purposes this is acceptable since all the numbers I am dealing with (standard measurements) are rational numbers anyway.

Solution 5

Buddies, can this help?

[]s


function toFraction($number) {
    if (!is_int($number)) {
        $number = floatval($number);
        $denominator = round(1 / $number);

        return "1/{$denominator}";
    }
    else {
        return $number;
    }
}
Share:
30,934

Related videos on Youtube

JD Isaacks
Author by

JD Isaacks

Author of Learn JavaScript Next github/jisaacks twitter/jisaacks jisaacks.com

Updated on July 09, 2022

Comments

  • JD Isaacks
    JD Isaacks almost 2 years

    I want the user to be able to type in a fraction like:

     1/2
     2 1/4
     3
    

    And convert it into its corresponding decimal, to be saved in MySQL, that way I can order by it and do other comparisons to it.

    But I need to be able to convert the decimal back to a fraction when showing to the user

    so basically I need a function that will convert fraction string to decimal:

    fraction_to_decimal("2 1/4");// return 2.25
    

    and a function that can convert a decimal to a faction string:

    decimal_to_fraction(.5); // return "1/2"
    

    How can I do this?

    • OMG Ponies
      OMG Ponies over 14 years
      As nice as it is for a user, you're asking for a lot of work vs defining three fields - whole number, nominator, and denominator.
    • DrYak
      DrYak over 14 years
      The problem is that, given the internal representation of floating points, you'll often end up with thing which are simple fraction, but don't have a simple aperiodic binary float representation. (Think 1/7 in decimal notation, without being able to use a periodicity notation). See here : en.wikipedia.org/wiki/Binary_numeral_system#Fractions_in_binβ€Œβ€‹ary
    • Tschallacka
      Tschallacka over 9 years
      if you want float precision up to big numbers take a look at this gist.github.com/anonymous/8ec4a38db78701e7bbc6 I adapted it so it can support precision up to the biggest int value. Could even make that with the big numbers math for unlimited precision.
  • JD Isaacks
    JD Isaacks over 14 years
    OK so I need to store the fraction and the decimal, but how do I gt the decimal to begin with?
  • Jir
    Jir over 14 years
    I'd be glad to have a comment explaining the motivations behind the "-1".
  • DrYak
    DrYak over 14 years
    Approximation: Indeed, this method won't work unless you use a better heuristic to determine the end as "integer number". Because in addition to the usual rounding problems of floating numbers, only fractions which are power of two (1/2, 1/4, 1/8, etc.) have an aperiodic binary notation. with everything else, you'll probably still have a remainder of 0.0001... even after correctly figuring out the denominator. (In binary float 10*0.1 is not exactly 1).
  • symcbean
    symcbean over 14 years
    Doh, there should be a ksort($fracs) at the end of init_fracs()
  • Stan
    Stan almost 9 years
    Thank you! Your function returns empty string if $input = "1" though.
  • xxstevenxo
    xxstevenxo over 8 years
    beautiful, loved that you used gcd.