Question

I'm trying to convert from decimal to fraction sporting odds. I have found via a search a PHP function which works well, but certain decimals cause problems, such as 2.1 which maxs out the server:

    function dec2frac($dec) { 

        $decBase = --$dec; 

        $div = 1; 

        do { 

            $div++; 

            $dec = $decBase * $div; 

        } while (intval($dec) != $dec); 

        if ($dec % $div == 0) { 
            $dec = $dec / $div; 
            $div = $div / $div; 
        } 

        return $dec.'/'.$div; 

    } 

$decimal = 2.3;

echo $decimal.' --> '.dec2frac($decimal);

A decimal odds of 6 should give 5/1. This is calulated as 6-1=5 = 5/1

I have found that decimal input of 2.2 and 2.3 trips the function up but other values seem to be ok. What is causing this anomaly, is there way around it?

Thanks.

Was it helpful?

Solution

This problem consists of two seperate steps

  • Create a fraction from a decimal number
  • Convert between betting odds and fractions

Let's start with the latter: A betting odd of 5 means, for every $1 invested, you get $5 if you win. Since you invested $1, your actual win is just $4. So the odds are 4-1 or 4/1

Analogous, betting odds of 2.5 mean, for every $1 you invest, you win $1.5, giving you 1.5-1 or 3-2 or 3/2

This leads us to the conclusion, that what we need is the fraction of ($odds-1)

Next part: Fractionizing. I didn't analyze the given algorithm, but wrote a very bad (but easily readable) one:

function dec2frac($val) {
     //first pump denominator up
     $tmp=strstr("$val",'.');
     if ($tmp) $tmp=strlen($tmp)-1;
     else $tmp=0;
     $n=$val;
     $d=1;
     for (;$tmp>0;$tmp--) {
       $n*=10;
       $d*=10;
     }
     $n=intval(round($n));
     $d=intval(round($d));

     //Now shorten the fraction

     //Find limit for pseudoprime search
     $min=$n; 
     if ($d<$n) $min=$d;
     $min=ceil($min/2);
     if (ceil($d/2)>$min) $min=ceil($d/2);
     if (ceil($n/2)>$min) $min=ceil($n/2);

     $pseudoprime=2;
     while ($pseudoprime<=$min) {
          //Shorten by current pseudoprime as long as possible
          while (true) {
               $nn=$n/$pseudoprime;
               if ($nn!=round($nn)) break;
               $dd=$d/$pseudoprime;
               if ($dd!=round($dd)) break;
               $n=intval($nn);
               $d=intval($dd);
          }
          //Move on to next pseudoprime
          $pseudoprime+=($pseudoprime==2)?1:2;
          if ($pseudoprime>3) 
            if (($pseudoprime/3)==floor($pseudoprime/3)) $pseudoprime+=2;
     }
     return "$n/$d";
}

This was tested to work with the values 0.25, 2.5, 3.1, 3.14, 3.141, 3.1415, 3.14159 and 3.141592.

The very unoptimized nature of the algorithm is a less important limit, as betting odds tend to have not very many decimal digits.

Together with

function odds2fract($odds) {
    return dec2frac($odds-1);
}

derived from the other step, we get successfull conversion of

5 --> 4/1
2.5 --> 3/2
2.1 --> 11/10
2.2 --> 6/5

Edit

The original version had a bug in the search limit calculation, which led to some fractions (e.g. completeley shortenable) failed to be shortened. The updated version fixes this.

Edit 2

Again a fixed bug: Failing to round() the values obtained in the first step before intval()ing them gave wrong results on fractions, that have a very bad fidelity in floatingpoint. Fixed by applying the missing round()

OTHER TIPS

The first thing that just immediately triggers is that you used '.1' in there. This suggests, to me, that we're dealing with floating point problems... check your code... yeah, looks like floating point problems to me. The problem here is that your numbers are stored in binary, and there's no convenient way for binary to use a lot of decimal values. When you start using simple math, you're not getting exact results, you're getting shortcut estimations.

Floating Point numbers in PHP

That link should give you a detailed run-down on what's going wrong, links to more pages that will give you still more information, and links to libraries that exist to fix it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top