We help IT Professionals succeed at work.

Convert decimal to fractional odds

infodigger
infodigger asked
on
Hello,

I have the following function which converts decimal odds to fractional odds. However, I have problems converting odds like 4.33 which should be 10/3 but it returns as 100/333.

Any ideas of how to fix this?

Thanks!
function odds($decimal)
{
    $decimal = $decimal - 1;
    if ($decimal == 0) {
        $whole = 0;
        $numerator = 0;
        $denominator = 1;
        $top_heavy = 0;
    } else {
        $sign = 1;
        if ($decimal < 0) {
            $sign = -1;
        }

        if (floor(abs($decimal)) == 0) {
            $whole = 0;
            $conversion = abs($decimal);
        } else {
            $whole = floor(abs($decimal));
            $conversion = abs($decimal);
        }

        $power = 1;
        $flag = 0;
        while ($flag == 0) {
            $argument = $conversion * $power;
            if ($argument == floor($argument)) {
                $flag = 1;
            } else {
                $power = $power * 10;
            }
        }

        $numerator = $conversion * $power;
        $denominator = $power;
        $hcf = euclid($numerator, $denominator);
        $numerator = $numerator / $hcf;
        $denominator = $denominator / $hcf;
        $whole = $sign * $whole;
        $top_heavy = $sign * $numerator;
        $numerator = abs($top_heavy) - (abs($whole) * $denominator);
        if (($whole == 0) && ($sign == -1)) {
            $numerator = $numerator * $sign;
        }


    }
    $top_heavy = round($top_heavy, 2);
    $denominator = round($denominator, 2);
    //return array($whole, $numerator, $denominator, $top_heavy);
    return number_format($top_heavy, 0, '.', '') . "/" . number_format($denominator,
        0, '.', '');

}
function euclid($number_one, $number_two)
{

    if (($number_one == 0) or ($number_two == 0)) {
        $hcf = 1;
        return $hcf;
    } else {
        if ($number_one < $number_two) {
            $buffer = $number_one;
            $number_one = $number_two;
            $number_two = $buffer;
        }

        $dividend = $number_one;
        $divisor = $number_two;
        $remainder = $dividend;
        while ($remainder > 0) {
            if ((floor($dividend / $divisor)) == ($dividend / $divisor)) {
                $quotient = $dividend / $divisor;
                $remainder = 0;
            } else {
                $quotient = floor($dividend / $divisor);
                $remainder = $dividend - ($quotient * $divisor);
            }
            $hcf = $divisor;
            $dividend = $divisor;
            $divisor = $remainder;
        }


    }
    return $hcf;
}

Open in new window

Comment
Watch Question

greetings infodigger, , I looked at your posted code, and I did not understand some of it. But I do not see what kind of mathematical result you want to get if you put in the value of  4.33, , you say that it should give the value of  10/3  which I take as meaning  "ten divided by 3" which gives a result value of  3.33 , NOT  4.33  , , if I put in a value of  4.33  , I would expect a return vale of 13/3 (but this is a Rounded Off approximation for the 4.333333333333  actual vale of  13/3
But maybe I do not understand what your problem is along mathematical logic thinking on your input and results, although clearly the  100/333 is incorrect, and I would guess has premature rounding errors
On top of that, the fractional representation of 4.33 is actually 433/100 because .33 is a rounded version of  1/3 which is a repeating decimal, Handling a rounded decimal will be difficult because what if I put in 1.13 how would you know that i mean 9/8 without knowing that i rounded .125 to .13?

Author

Commented:
Hi Slick812,

First of all thank you for stopping by to help me with my problem.

There are two different type of odds for a sports event. The decimal odds and the fractional odds. The fractional odds are used in the UK.

Here is an example:
Fractional odds of 1/2 imply that the bet would pay out 1 unit for every 2 units risked, while fractional odds of 2/1 imply that the bet would pay out $2 for every $1 risked.

So 1/2 in fractional would be 1.5 in decimal.

As you write the result would be 13/3 but we should subtract 1 = 3/3 so the actual return should be 10/3. Even if I had 4.33333333333 as input I couldn't manage to return 10/3 with my code.

There is a javascript tool that does the job well here http://www.sbrforum.com/betting-tools/odds-converter/ but I can't translate this properly in PHP.

Also take a look at a simpler code I have found but still with the same problem:

 
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;
}

Open in new window


Thanks!
thanks infodigger, for your explanation, I do not place bets using the language of "decimal odds to fractional odds", but I somewhat understand it now, , I looked for javascript at your link, but I could only find Mutibyte text javascript in the odds.js file, and don't have time to mess with it now

I may can redo the math in your function, however, To be more sure of the results you need , if 1.5 went in then  1/2 would be output?
I assume that input values less than ONE (0.5) are not allowed, or how is that translated?
what output would you want for an input of =
2.5
4.2
5.6

sorry for asking, but this is new to me
Most Valuable Expert 2011
Top Expert 2016

Commented:
Reading this: Fractional odds of 1/2 imply that the bet would pay out 1 unit for every 2 units risked, while fractional odds of 2/1 imply that the bet would pay out $2 for every $1 risked.  I am thinking that the simplest way of thinking about this is that the amount of the bet is multiplied by the fractional odds and that determines the payout.

Reading this: So 1/2 in fractional would be 1.5 in decimal.  I am thinking that a better expression might be 1 : 0.5, meaning that for every one bet, the payout is 0.5.  So for a bet of 12, the payout would be 6.

If 4.33 could be read to mean 4 : 0.33, thus a bet of 4 would either pay 4*0.33 or 4/0.33.  Which would be the right way to interpret this expression?

Author

Commented:
Slick812,

Yes, values less than 1 do not have meaning.

1.5 -> 1/2
2.5 -> 3/2
4.2 -> 16/5
5.6 -> 28/5

So let's say you bet $5 at the last event (5.6). Then you get back 28.

Thanks for your time!

Author

Commented:
Ray,

Actually the number in the example 1.5 -> 1/2 are not a good idea to create a rule like that. The decimal place of the decimal odds does not work like that (4.33 -> 4: 0.33).

The two odds are the same but with different representation. For example:

Decimal odds 5.6. Means that you bet $1 and if you win you get $5.6.
In fractional it's 23/5. Meaning if you bet $5 you get $23, which is the same. The problem is to find the "right" integer part to present a decimal odd the correct way.

For example to convert it you start by saying it should be 5.6/1. But 5.6 is not integer so you have to multiply. And then you find that multiplying by 5 gives you the first integer which is 28. So 28/5. Then you subtract 1=5/5 -> 23/5

However if you have an odd like 4.33 you can't know that it was 4.33333333 so even if you multiply it by 3 you don't get an integer but close to an integer. So the idea is that somehow you should have some kind of "precision" allowance at the function which could say that 4.33 = 12.99 = 13/3. Then 13/3 - 3/3 = 10/3.

Author

Commented:
Sorry made a mistake for 5.6 -> 28/5

It is actually 23/5 (forgot to subtract 1)
Most Valuable Expert 2011
Top Expert 2016

Commented:
Help me understand this one: 4.2 -> 16/5

What is the payout for a bet of $1, $2 and $3, please?

Author

Commented:
You bet $1 you get 4.2
$2 -> 8.4
$3 -> 12,6

But in fractional odds the profit is displayed. That's why you deduct 1.

So UK people understand that if you bet 5 you get 16 as profit. While the rest understand that if you bet 5 you get a total of 21 back.

Author

Commented:
Or simpler said: Odds in decimal format express the amount that would be returned from a $1 bet *inclusive* of original stake.
Most Valuable Expert 2011
Top Expert 2016

Commented:
This part is also confusing to me: subtract 1=5/5 -> 23/5  Am I on firm ground in understanding it this way:

1 bet at 5.6 pays 5.6.  Now subtracting the one, we get 4.6 payout
2 bet at 5.6 pays 11.2.  Now subtracting the two, we get 9.2 payout
3 bet at 5.6 pays 16.8.  Now subtracting the three, we get 13.8 payout
4 bet at 5.6 pays 22.4.  Now subtracting the four, we get 18.4 payout
5 bet at 5.6 pays 28.  Now subtracting the five, we get 23 payout
Most Valuable Expert 2011
Top Expert 2016

Commented:
So it would seem that we need to know not only the payout ratio, but also the amount of the wager in order to compute the profit.  Is that understanding right?

Author

Commented:
Yes, but that doesn't change the odds. But to calculate the profit, yes you need the wager amount.
I did some testing with your "simple function" and it seems to work except it does sometimes has a "Higher" value for the $dec value than is correct, , I di NOT test this very much, but I did go to the site you gave
http://www.sbrforum.com/betting-tools/odds-converter/

and did a few values and got back the fraction bet that it gave on their site

2.8  9/5
4.6  18/5

and a few others, ,
try code below and see if it give wrong results for your tests?

Also, as I remember in betting there are some values like 2/1  and  3/1  that are very often used, This math function is not complex, but I think I would have and array with the most common Decimal Bet values as keys, and just return that frac value, without doing any math, maybe?
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;
    }
if ($dec > 19){$dec = floor($dec / 10);}
    return $dec . '/' . $div;
}

Open in new window

Author

Commented:
I still have the same problem with 4.33.

Yes the idea of having the most common ones in an array is an option but there are too many and you can't really be sure that all decimals can be converted by that way.

I have to leave for a few hours, thank you very much for all the help, I really appreciate it since it is a very difficult/unknown subject. I will leave the question open until I get back if anyone comes up with something:):)
after re - reading your posts, I saw that maybe I need another value? ?  so You may need to change my line of
if ($dec > 19){$dec = floor($dec / 10);}

to

if ($dec > 29){$dec = floor($dec / 10);}

however this does not change the math error
revized function below, works on 4.33 now
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;
    }
if ($dec > 99){$dec = floor($dec / 100); $div = floor($div / 10);}
if ($dec > 29){$dec = floor($dec / 10);}
    return $dec . '/' . $div;
}

Open in new window

I have been trying to find the math error in the simple and the complex code functions that you gave, however, if I increase the decimal points to 2 or 3  as in  4.33  or 4.333 , , then I get completely constipated mentally and the results do not match the ones at
http://www.sbrforum.com/betting-tools/odds-converter/

I am out of time for now, I may do some later or tomorrow, I feel that I will need to come at this from a reverse perspective as I will do a formula to make  23/5  into  5.6  and then do a reverse of that, the while loops in your conversions do not do math so much as they narrow down results to get some sort of near estimate for the return value
Most Valuable Expert 2011
Top Expert 2016
Commented:
I got distracted by some other stuff, but this is what I came up with.  It has not been tested very much...
http://www.laprbass.com/RAY_temp_infodigger.php

HTH, ~Ray
<?php // RAY_temp_infodigger.php
error_reporting(E_ALL);
echo "<pre>";

// TEST DATA SET
$testdata = array
( '1.5' => '1/2'
, '2.5' => '3/2'
, '4.2' => '16/5'
, '5.6' => '23/5'
, '4.3' => '10/3'
, '4.333' => '10/3'
)
;

// VISUALIZE THE TEST DATA
var_dump($testdata);

// TEST EACH VALUE
foreach ($testdata as $dec => $frac)
{
    echo PHP_EOL . "$dec $frac ==> ";
    echo infodigger($dec);
}


// A FUNCTION
function infodigger($n)
{
    // COMPUTE AN ACCEPTABLY CLOSE APPROXIMATION OF AN INTEGER
    $a = explode('.', "$n");
    $d = strlen($a[1]);

    // TENTH OF THE DECIMAL
    $f = "0." . str_pad(NULL, $d, '0') . '1';
    $f = (float)$f;
    $g = $f * 10.0;

    // SOMEWHAT ARBITRARY LIMIT ON THE ITERATOR
    $thing = 100;

    // DETERMINE THE MULTIPLIER
    $facto = 1;
    $x     = 0.0;
    while ($thing)
    {
        $x  = $x + $n;
        $x  = number_format($x, $d);
        $z  = $x + $g;
        $z  = number_format($z, $d);
        $fx = floor($x);
        $fz = floor($z);
        if ( $x == $fx ) break;
        if ( $z == $fz ) break;
        $facto++;
        $thing--;
    }

    // IF THE ITERATOR EXPIRED WE ARE HOSED
    if (!$thing) return FALSE;

    $new = (int)round($n * $facto)-$facto;
    return "$new" . '/' . "$facto";
}

Open in new window

Author

Commented:
Thank you all very much! I think this has been one of the best discussions so far on EE and I really got some good answers and I can take it from there with just a few tweaks and make it work properly.
it took more time than I thought, I did the reverse and did it opposite, unfortunately this requires getting a greatest common denominator, which was a tremendous problem for me, as I had not done any thing like that before. THid seems to work as far as I have tested it, and it is currentlty limited to TWO decimal places , and might do three, but I do not have time too test that

I have it as  function dec2fracBet($dec)
I will do  2.1  and 2.11      but NOT 2.111   if you enter 2.111  it will return  '1/1' as an ERROR indicator


if you want to fully test it with 3 places, you can change
if ($len > 2) return '1/1';

to

if ($len > 3) return '1/1';
<?php

function bcgcd($v1, $v2){
if ($v1 < $v2){$sw = $v1;
	$v1 = $v2;
	$v2 = $sw;}
$md = 1;
while ($md != 0){
	$md = bcmod($v1, $v2);
	$v1 = $v2;
	$v2 = $md;
    }
return $v1;
} 

function dec2fracBet($dec){--$dec;
$inv = intval($dec);
$tl = $dec-$inv;
$strF = ''.$tl;
$len = (strlen($strF)-2);//*10;
if ($len > 2) return '1/1';
$len = pow(10, $len);
$strF = $dec*$len;
$primeA = array(2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,
	79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,
	179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,
	277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,
	389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,
	499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,
	617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,
	739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,
	859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997);
if(($strF == $len) || ($strF == 0) || ($len == 0)) return '1/1';
if (in_array($strF, $primeA)) $inv = 1; else $inv = bcgcd($len, $strF);

$dec = $len / $inv;
$tl = $strF / $inv;
echo 'ZZ len= ',$len,' tl= ',$tl,' strF ',$strF,' gcd2 ',$inv,'- ',' dec ',$dec,'- ';
return $tl . '/' . $dec;
}

$cn = 4.6;
echo '<br>',dec2fracBet($cn);
$cn = 4.36;
echo '<br>',dec2fracBet($cn);

Open in new window

Author

Commented:
Slick812,

Thank you very much. This is a smart approach and probably the one I will use in the end!