Тайм-аут конверсии десятичных коэффициентов
Я пытаюсь конвертировать из десятичных в дробные спортивные шансы. Я нашел с помощью поиска функцию PHP, которая работает хорошо, но некоторые десятичные дроби вызывают проблемы, такие как 2.1, который максимизирует сервер:
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);
Десятичные шансы 6 должны давать 5/1. Это рассчитывается как 6-1=5 = 5/1
Я обнаружил, что десятичный ввод 2,2 и 2,3 отключает функцию, но другие значения, кажется, в порядке. Что вызывает эту аномалию, есть ли способ обойти это?
Благодарю.
2 ответа
Эта проблема состоит из двух отдельных шагов
- Создать дробь из десятичного числа
- Конвертировать между коэффициентами ставок и долей
Давайте начнем с последнего: ставки нечетные 5
означает, что за каждый вложенный 1 доллар вы получаете 5 долларов, если выиграете. Поскольку вы вложили 1 доллар, ваш фактический выигрыш составляет всего 4 доллара. Таким образом, шансы 4-1 или 4/1
Аналогичные, ставки на 2.5
значит, за каждый вложенный 1 доллар вы выигрываете 1,5 доллара, что дает вам 1,5-1 или 3-2 или 3/2
Это приводит нас к выводу, что нам нужна доля ($odds-1)
Следующая часть: Фракционирование. Я не анализировал данный алгоритм, но написал очень плохой (но легко читаемый):
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";
}
Это было проверено на работу со значениями 0,25, 2,5, 3,1, 3,14, 3,141, 3,1415, 3,14159 и 3,141592.
Очень неоптимизированная природа алгоритма является менее важным ограничением, так как шансы на ставку, как правило, имеют не очень много десятичных цифр.
Вместе с
function odds2fract($odds) {
return dec2frac($odds-1);
}
полученный из другого шага, мы получаем успешное преобразование
5 --> 4/1
2.5 --> 3/2
2.1 --> 11/10
2.2 --> 6/5
редактировать
В исходной версии была ошибка в расчете предела поиска, что приводило к тому, что некоторые дроби (например, сокращаемые полные данные) не могли быть сокращены. Обновленная версия исправляет это.
Редактировать 2
Опять исправлена ошибка: round()
значения, полученные на первом этапе перед intval()
Их использование дало неверные результаты для дробей, которые имеют очень плохую точность в плавающей точке. Исправлено путем применения отсутствующего round()
Первое, что сразу же срабатывает, это то, что вы использовали там ".1". Для меня это говорит о том, что мы имеем дело с проблемами с плавающей запятой... проверьте ваш код... да, для меня это похоже на проблемы с плавающей запятой. Проблема здесь в том, что ваши числа хранятся в двоичном виде, и для двоичного кода нет удобного способа использовать много десятичных значений. Когда вы начинаете использовать простую математику, вы не получаете точных результатов, вы получаете краткие оценки.
Числа с плавающей точкой в PHP
Эта ссылка должна дать вам подробное описание того, что происходит не так, ссылки на дополнительные страницы, которые дадут вам еще больше информации, и ссылки на библиотеки, которые существуют, чтобы исправить это.