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.
This problem consists of two seperate steps
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()