PHP преобразовывают десятичное число в часть и назад?

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

 1/2
 2 1/4
 3

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

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

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

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

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

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

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

21
задан Flexo 15 October 2011 в 10:38
поделиться

6 ответов

Я думаю, что я бы тоже сохранил строковое представление, как только вы запустите математику , вы не получите его обратно!

И вот функция быстрых и грязных вычислений, никаких гарантий:

$input = '1 1/2';
$fraction = array('whole' => 0);
preg_match('/^((?P<whole>\d+)(?=\s))?(\s*)?(?P<numerator>\d+)\/(?P<denominator>\d+)$/', $input, $fraction);
$result = $fraction['whole'] + $fraction['numerator']/$fraction['denominator'];
print_r($result);die;

О, для полноты, добавьте проверку, чтобы убедиться, что $ дробь ['знаменатель' ]! = 0 .

19
ответ дан 29 November 2019 в 06:54
поделиться

Чтобы можно было использовать класс PEAR Math_Fraction для некоторых ваших потребностей

<?php

include "Math/Fraction.php";

$fr = new Math_Fraction(1,2);


// print as a string
// output: 1/2
echo $fr->toString();

// print as float
// output: 0.5
echo $fr->toFloat();

?>
8
ответ дан 29 November 2019 в 06:54
поделиться

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

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

0
ответ дан 29 November 2019 в 06:54
поделиться

Дробь до десятичной запятой достаточно проста и решений много. Я бы предложил обрезку строки, замену пробелов на '+', и все, что угодно, кроме пробела,/, или цифр на '', а затем прогонял бы ее через '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
ответ дан 29 November 2019 в 06:54
поделиться

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

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

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

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

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

.
0
ответ дан 29 November 2019 в 06:54
поделиться

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

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

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

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

.
0
ответ дан 29 November 2019 в 06:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: