PHP convert decimal into fraction and back?
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;
}
}
Related videos on Youtube
JD Isaacks
Author of Learn JavaScript Next github/jisaacks twitter/jisaacks jisaacks.com
Updated on July 09, 2022Comments
-
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 over 14 yearsAs 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 over 14 yearsThe 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 over 9 yearsif 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 over 14 yearsOK so I need to store the fraction and the decimal, but how do I gt the decimal to begin with?
-
Jir over 14 yearsI'd be glad to have a comment explaining the motivations behind the "-1".
-
DrYak over 14 yearsApproximation: 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 over 14 yearsDoh, there should be a ksort($fracs) at the end of init_fracs()
-
Stan almost 9 yearsThank you! Your function returns empty string if $input = "1" though.
-
xxstevenxo over 8 yearsbeautiful, loved that you used gcd.