PHP convert decimal into fraction and back?

前端 未结 14 794
面向向阳花
面向向阳花 2020-12-05 07:43

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,

相关标签:
14条回答
  • 2020-12-05 07:58

    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;
    }
    
    0 讨论(0)
  • 2020-12-05 08:02

    A variation of Jir's approach could actually work if only a limited amount of denominators are used : multiply everything by the least common denominators (and round the result to discard any remaining decimals due to approximation).

    I.e.: if you only have to deal with halfs, thrids and quarters, just multiply everything by 12.

    And also if you know the common denominator, this should greatly reduce the search speed by knowing exactly which numbers to search instead of searching all n+1 possible.

    If you have to deal with lots of unusual fractions, like 1/7, 1/13, etc. well, stick to Derek's solution and store the original value too.

    0 讨论(0)
  • 2020-12-05 08:04

    I made a blog post with a couple solutions for this, the most recent approach I took is: http://www.carlosabundis.com/2014/03/25/converting-decimals-to-fractions-with-php-v2/

        function dec2fracso($dec){
        //Negative number flag.
        $num=$dec;
        if($num<0){
            $neg=true;
        }else{
            $neg=false;
        }
    
        //Extracts 2 strings from input number
        $decarr=explode('.',(string)$dec);
    
        //Checks for divided by zero input.
        if($decarr[1]==0){
            $decarr[1]=1;
            $fraccion[0]=$decarr[0];
            $fraccion[1]=$decarr[1];
            return $fraccion;
        }
    
        //Calculates the divisor before simplification.
        $long=strlen($decarr[1]);
        $div="1";
        for($x=0;$x<$long;$x++){
            $div.="0";
        }
    
        //Gets the greatest common divisor.
        $x=(int)$decarr[1];
        $y=(int)$div;
        $gcd=gmp_strval(gmp_gcd($x,$y));
    
        //Calculates the result and fills the array with the correct sign.
        if($neg){
            $fraccion[0]=((abs($decarr[0])*($y/$gcd))+($x/$gcd))*(-1);
        }else{
            $fraccion[0]=(abs($decarr[0])*($y/$gcd))+($x/$gcd);
        }
        $fraccion[1]=($y/$gcd);
        return $fraccion;
    }
    
    0 讨论(0)
  • 2020-12-05 08:07

    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.

    0 讨论(0)
  • 2020-12-05 08:07

    An approach would be to retrieve the decimal value and multiply it by 2, 3, 4 and so on until you get an integer number.

    However, I'd stick with the answer given by Derek. Guess what happens when a user inserts n/(n+1) with n high. Such an algorithm would have to scan all the numbers up to n+1. Not to mention it is likely you'll end up with approximation problems.

    0 讨论(0)
  • 2020-12-05 08:07

    You'll have to face a serious problem, because floats are not precise enough.

    When you'll have to deal with 1.3333, PHP will make an estimate of this value... So you will never be able to convert it to 1 1/3.

    It seems to be simple to overcome, but if you want your program to differentiate 1/7901 (~ 1,2656625743576762435134793064169e-4) with 1/7907 (~ 1,2647021626406981155937776653598e-4) precisely... this will be a real hell !!

    IMHO, if you want to deal with maths, you should rely on an external library... or try to make PHP communicate with Matlab.

    If you want to know more, i suggest you dig in floating point problems... Starting with wikipedia.

    0 讨论(0)
提交回复
热议问题