Converting float decimal to fraction

前端 未结 6 861
粉色の甜心
粉色の甜心 2020-12-05 15:32

I am trying to convert calculations keyed in by users with decimal results into fractions. For e.g.; 66.6666666667 into 66 2/3. Any pointers? Thanx in advance

相关标签:
6条回答
  • 2020-12-05 16:07

    Farey fractions can be quite useful in this case.

    They can be used to convert any decimal into a fraction with the lowest possible denominator.

    Sorry - I don't have a prototype in PHP, so here's one in Python:

    def farey(v, lim):
        """No error checking on args.  lim = maximum denominator.
            Results are (numerator, denominator); (1, 0) is 'infinity'."""
        if v < 0:
            n, d = farey(-v, lim)
            return (-n, d)
        z = lim - lim   # Get a "zero of the right type" for the denominator
        lower, upper = (z, z+1), (z+1, z)
        while True:
            mediant = (lower[0] + upper[0]), (lower[1] + upper[1])
            if v * mediant[1] > mediant[0]:
                if lim < mediant[1]:
                    return upper
                lower = mediant
            elif v * mediant[1] == mediant[0]:
                if lim >= mediant[1]:
                    return mediant
                if lower[1] < upper[1]:
                    return lower
                return upper
            else:
                if lim < mediant[1]:
                    return lower
                upper = mediant
    
    0 讨论(0)
  • 2020-12-05 16:08

    Converted Python code in answer from @APerson241 to PHP

    <?php
    function farey($v, $lim) {
        // No error checking on args.  lim = maximum denominator.
        // Results are array(numerator, denominator); array(1, 0) is 'infinity'.
        if($v < 0) {
            list($n, $d) = farey(-$v, $lim);
            return array(-$n, $d);
        }
        $z = $lim - $lim;   // Get a "zero of the right type" for the denominator
        list($lower, $upper) = array(array($z, $z+1), array($z+1, $z));
        while(true) {
            $mediant = array(($lower[0] + $upper[0]), ($lower[1] + $upper[1]));
            if($v * $mediant[1] > $mediant[0]) {
                if($lim < $mediant[1]) 
                    return $upper;
                $lower = $mediant;
            }
            else if($v * $mediant[1] == $mediant[0]) {
                if($lim >= $mediant[1])
                    return $mediant;
                if($lower[1] < $upper[1])
                    return $lower;
                return $upper;
            }
            else {
                if($lim < $mediant[1])
                    return $lower;
                $upper = $mediant;
            }
        }
    }
    
    // Example use:
    $f = farey(66.66667, 10);
    echo $f[0], '/', $f[1], "\n"; # 200/3
    $f = farey(sqrt(2), 1000);
    echo $f[0], '/', $f[1], "\n";  # 1393/985
    $f = farey(0.43212, 2000);
    echo $f[0], '/', $f[1], "\n";  # 748/1731
    
    0 讨论(0)
  • 2020-12-05 16:09

    Continued fractions can be used to find rational approximations to real numbers that are "best" in a strict sense. Here's a PHP function that finds a rational approximation to a given (positive) floating point number with a relative error less than $tolerance:

    <?php
    function float2rat($n, $tolerance = 1.e-6) {
        $h1=1; $h2=0;
        $k1=0; $k2=1;
        $b = 1/$n;
        do {
            $b = 1/$b;
            $a = floor($b);
            $aux = $h1; $h1 = $a*$h1+$h2; $h2 = $aux;
            $aux = $k1; $k1 = $a*$k1+$k2; $k2 = $aux;
            $b = $b-$a;
        } while (abs($n-$h1/$k1) > $n*$tolerance);
    
        return "$h1/$k1";
    }
    
    printf("%s\n", float2rat(66.66667)); # 200/3
    printf("%s\n", float2rat(sqrt(2)));  # 1393/985
    printf("%s\n", float2rat(0.43212));  # 748/1731
    

    I have written more about this algorithm and why it works, and even a JavaScript demo here: https://web.archive.org/web/20180731235708/http://jonisalonen.com/2012/converting-decimal-numbers-to-ratios/

    0 讨论(0)
  • 2020-12-05 16:19

    Here is my approach to this problem. Works fine with rational numbers.

    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 16:25

    Based upon @Joni's answer, here is what I used to pull out the whole number.

    function convert_decimal_to_fraction($decimal){
    
        $big_fraction = float2rat($decimal);
        $num_array = explode('/', $big_fraction);
        $numerator = $num_array[0];
        $denominator = $num_array[1];
        $whole_number = floor( $numerator / $denominator );
        $numerator = $numerator % $denominator;
    
        if($numerator == 0){
            return $whole_number;
        }else if ($whole_number == 0){
            return $numerator . '/' . $denominator;
        }else{
            return $whole_number . ' ' . $numerator . '/' . $denominator;
        }
    }
    
    function float2rat($n, $tolerance = 1.e-6) {
        $h1=1; $h2=0;
        $k1=0; $k2=1;
        $b = 1/$n;
        do {
            $b = 1/$b;
            $a = floor($b);
            $aux = $h1; $h1 = $a*$h1+$h2; $h2 = $aux;
            $aux = $k1; $k1 = $a*$k1+$k2; $k2 = $aux;
            $b = $b-$a;
        } while (abs($n-$h1/$k1) > $n*$tolerance);
    
        return "$h1/$k1";
    }
    
    0 讨论(0)
  • 2020-12-05 16:27

    Sometimes it is necessary to treat only the decimals of a float. So I created a code that uses the function created by @Joni to present a format that is quite common in culinary recipes, at least in Brazil.

    So instead of using 3/2 which is the result for 1.5, using the function I created it is possible to present the value 1 1/2, and if you want, you can also add a string to concatenate the values, creating something like "1 and 1/2 ".

    function float2rat($n, $tolerance = 1.e-6) {
      $h1=1; $h2=0;
      $k1=0; $k2=1;
      $b = 1/$n;
      do {
          $b = 1/$b;
          $a = floor($b);
          $aux = $h1; $h1 = $a*$h1+$h2; $h2 = $aux;
          $aux = $k1; $k1 = $a*$k1+$k2; $k2 = $aux;
          $b = $b-$a;
      } while (abs($n-$h1/$k1) > $n*$tolerance);
    
      return "$h1/$k1";
    }
    
    function float2fraction($float, $concat = ' '){
      
      // ensures that the number is float, 
      // even when the parameter is a string
      $float = (float)$float;
    
      if($float == 0 ){
        return $float;
      }
      
      // when float between -1 and 1
      if( $float > -1 && $float < 0  || $float < 1 && $float > 0 ){
        $fraction = float2rat($float);
        return $fraction;
      }
      else{
    
        // get the minor integer
        if( $float < 0 ){
          $integer = ceil($float);
        }
        else{
          $integer = floor($float);
        }
    
        // get the decimal
        $decimal = $float - $integer;
    
        if( $decimal != 0 ){
    
          $fraction = float2rat(abs($decimal));
          $fraction = $integer . $concat . $fraction;
          return $fraction;
        }
        else{
          return $float;
        }
      }
    }
    

    Usage e.g:

    echo float2fraction(1.5);
    will return "1 1/2"
    
    0 讨论(0)
提交回复
热议问题