PHP конвертировать десятичную дробь и обратно? - PullRequest
21 голосов
/ 23 декабря 2009

Я хочу, чтобы пользователь мог набирать дробь, например:

 1/2
 2 1/4
 3

И преобразовать его в соответствующее ему десятичное число, чтобы сохранить в MySQL, чтобы я мог заказать его и сделать другие сравнения с ним.

Но мне нужно иметь возможность преобразовывать десятичную дробь обратно в дробь при показе пользователю

так что в основном мне нужна функция, которая преобразует дробную строку в десятичную:

fraction_to_decimal("2 1/4");// return 2.25

и функция, которая может преобразовывать десятичную дробь в строку фракции:

decimal_to_fraction(.5); // return "1/2"

Как я могу это сделать?

Ответы [ 14 ]

0 голосов
/ 23 декабря 2009

Дробь в десятичную дробь довольно проста, и существует множество решений. Я бы пошел с обрезкой строки, заменой пробелов на «+» и всего, кроме пробела, / ,. или цифры с '', а затем запустить его через 'eval'.

Десятичные дроби практически невозможно сделать правильно - не в последнюю очередь потому, что ваша десятичная дробь, вероятно, должна быть сначала преобразована в двоичную - в этот момент вы теряете большую точность. В качестве академического упражнения ..... Если вы можете жить с разницей между 20976/41953 и 1/2, то вы можете попробовать нечеткое совпадение для заранее определенного числа фракций:

(возможно, есть более удобный способ реализации того же алгоритма, но я оставлю это в качестве упражнения для читателя).

define('DECIMAL_DIGITS',5);

function decimal_2_frac($inp_decimal)
{
  static $fracs;
  if (!is_array($fracs)) {
    init_fracs($fracs);
  }
  $int_part=(integer)$inp_decimal;
  $inp_decimal=$inp_decimal-$int_part;
  $candidate='';
  $distance=10;
  foreach ($fracs as $decimal=>$frac) {
     if (abs($decimal-$inp_decimal)<$distance) {
       $candidate=$frac;
       $distance=abs($decimal-$inp_decimal);
     }
  if (abs($decimal-$inp_decimal)>$distance) {
     break;
  }
 }
 return $int_part . ' ' . $candidate;
}

function init_fracs(&$fracs)
{
   $fracs=array(); 
   for ($x=2;$x<(5*DECIMAL_DIGITS);$x++) {
       // there's probably a beter way to calculate the loop limit
      for ($y=1; $y<$x; $y++) {
         $decimal=round($y/$x,DECIMAL_DIGITS);
         $frac="$x/$y";
         if (!array_key_exists($decimal,$fracs)) {
         $fracs[$decimal]=$frac;
   }
  }    
 }
}

Но лично я бы просто сохранил исходное представление в отдельном поле в базе данных.

0 голосов
/ 23 декабря 2009

Вариант подхода Джира может сработать, если используется только ограниченное количество знаменателей: умножить все на наименьшие общие знаменатели (и округлить результат, чтобы отбросить все оставшиеся десятичные дроби из-за аппроксимации).

Т.е.: если вам нужно иметь дело только с половинками, третями и четвертями, просто умножьте все на 12.

А также, если вы знаете общий знаменатель, это должно значительно снизить скорость поиска, точно зная, какие числа искать вместо поиска всех n + 1 возможных.

Если вам приходится иметь дело с множеством необычных дробей, таких как 1/7, 1/13 и т. Д., Хорошо, придерживайтесь решения Дерека и сохраняйте исходное значение тоже.

0 голосов
/ 23 декабря 2009

Вам придется столкнуться с серьезной проблемой, потому что поплавки не достаточно точны.

Когда вам придется иметь дело с 1.3333, PHP оценит это значение ... Поэтому вы никогда не сможете преобразовать его в 1 1/3.

Кажется, что это легко преодолеть, но если вы хотите, чтобы ваша программа дифференцировала 1/7901 (~ 1,2656625743576762435134793064169e-4) с 1/7907 (~ 1,2647021626406981155937776653598e-4) точно ... это будет настоящий ад !!

ИМХО, если вы хотите заниматься математикой, вам следует полагаться на внешнюю библиотеку ... или попытаться заставить PHP общаться с Matlab.

Если вы хотите узнать больше, я предлагаю вам заняться проблемами с плавающей запятой ... Начиная с wikipedia .

0 голосов
/ 23 декабря 2009

Подход заключается в том, чтобы извлечь десятичное значение и умножить его на 2, 3, 4 и т. Д., Пока не получите целое число.

Однако я бы придерживался ответа Дерека. Угадайте, что происходит, когда пользователь вставляет n / (n + 1) с n высоким. Такой алгоритм должен был бы сканировать все числа до n + 1. Не говоря уже о том, что у вас могут возникнуть проблемы с аппроксимацией.

...