22

ユーザーが次のような分数を入力できるようにしたい:

 1/2
 2 1/4
 3

そして、それを対応する 10 進数に変換して MySQL に保存します。そうすれば、それで注文したり、他の比較を行うことができます。

しかし、ユーザーに表示するときに、小数を分数に戻すことができる必要があります

したがって、基本的には、分数文字列を 10 進数に変換する関数が必要です。

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

小数を派閥文字列に変換できる関数:

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

これどうやってするの?

4

15 に答える 15

25

場合によっては、それを行う方法を見つける必要があり、丸めが許容されます。したがって、どの範囲の丸めが適切かを判断すると、次のような関数を作成できます。小数を最も近い分数に変換すること。テストする分母を追加することで、精度を拡張できます。

function decToFraction($float) {
    // 1/2, 1/4, 1/8, 1/16, 1/3 ,2/3, 3/4, 3/8, 5/8, 7/8, 3/16, 5/16, 7/16,
    // 9/16, 11/16, 13/16, 15/16
    $whole = floor ( $float );
    $decimal = $float - $whole;
    $leastCommonDenom = 48; // 16 * 3;
    $denominators = array (2, 3, 4, 8, 16, 24, 48 );
    $roundedDecimal = round ( $decimal * $leastCommonDenom ) / $leastCommonDenom;
    if ($roundedDecimal == 0)
        return $whole;
    if ($roundedDecimal == 1)
        return $whole + 1;
    foreach ( $denominators as $d ) {
        if ($roundedDecimal * $d == floor ( $roundedDecimal * $d )) {
            $denom = $d;
            break;
        }
    }
    return ($whole == 0 ? '' : $whole) . " " . ($roundedDecimal * $denom) . "/" . $denom;
}
于 2012-02-04T18:54:08.323 に答える
19

文字列表現も保存すると思います。一度計算を実行すると、元に戻せないからです。

そして、これは迅速でダーティな計算関数であり、保証はありません。

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

ああ、完全を期すために、確認のためにチェックを追加してください$fraction['denominator'] != 0

于 2009-12-23T17:03:33.280 に答える
8

一部のニーズに合わせて 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();

?>
于 2009-12-23T17:08:51.253 に答える
5

これは、最初に有効な分数を決定するソリューションです (ただし、必ずしも最も単純な分数ではありません)。つまり、0.05 -> 5/100 です。次に、分子と分母の最大公約数を決定して、最も単純な分数である 1/20 に減らします。

function decimal_to_fraction($fraction) {
  $base = floor($fraction);
  $fraction -= $base;
  if( $fraction == 0 ) return $base;
  list($ignore, $numerator) = preg_split('/\./', $fraction, 2);
  $denominator = pow(10, strlen($numerator));
  $gcd = gcd($numerator, $denominator);
  $fraction = ($numerator / $gcd) . '/' . ($denominator / $gcd);
  if( $base > 0 ) {
    return $base . ' ' . $fraction;
  } else {
    return $fraction;
  }
}

# Borrowed from: http://www.php.net/manual/en/function.gmp-gcd.php#69189
function gcd($a,$b) {
  return ($a % $b) ? gcd($b,$a % $b) : $b;
}

これには gcd の純粋な PHP 実装が含まれていますが、 gmp モジュールがインストールされていることが確実な場合は、gcd に付属しているものを使用できます。

他の多くの人が指摘しているように、有理数を使用する必要があります。したがって、1/7 を 10 進数に変換してから 10 進数に戻そうとすると、精度が失われて 1/7 に戻せなくなり、うまくいきません。とにかく、私が扱っているすべての数値(標準測定値)は有理数であるため、私の目的ではこれは受け入れられます。

于 2013-06-14T20:46:37.737 に答える
2

上記を少し改善しましたが、シンプルに保ちます。

function dec2frac($f) {
  $base = floor($f);
  if ($base) {
    $out = $base . ' ';
    $f = $f - $base;
  }
  if ($f != 0) {
    $d = 1;
    while (fmod($f, 1) != 0.0) {
      $f *= 2;
      $d *= 2;
    }
    $n = sprintf('%.0f', $f);
    $d = sprintf('%.0f', $d);
    $out .= $n . '/' . $d;
  }
  return $out;
}
于 2013-02-19T10:38:41.760 に答える
2

仲間、これは役に立ちますか?

[] 秒


function toFraction($number) {
    if (!is_int($number)) {
        $number = floatval($number);
        $denominator = round(1 / $number);

        return "1/{$denominator}";
    }
    else {
        return $number;
    }
}
于 2013-02-15T00:19:55.430 に答える
0

限られた量の分母のみが使用されている場合、Jirのアプローチのバリエーションが実際に機能する可能性があります。つまり、すべてに最小公分母を掛けます(そして、結果を丸めて、近似のために残りの小数を破棄します)。

つまり、半分、3倍、4分の1だけを処理する必要がある場合は、すべてに12を掛けます。

また、最小公分母を知っている場合は、可能なすべてのn + 1を検索するのではなく、検索する番号を正確に知ることで、検索速度を大幅に低下させるはずです。

1 / 7、1 / 13など、多くの異常な分数をうまく処理する必要がある場合は、Derekのソリューションに固執し、元の値も保存します。

于 2009-12-23T17:45:13.850 に答える
0
function dec2frac($f)
{
    $d = 1

    while (fmod($f, 1) != 0.0) {
        $f *= 2;
        $d *= 2;
    }

    $n = sprintf('%.0f', $f);
    $d = sprintf('%.0f', $d);

    return array($n, $d);
}

それで$f == $n / $d

例えば:

print_r(dec2frac(3.1415926));

出力:

Array
(
    [0] => 3537118815677477  // $n
    [1] => 1125899906842624  // $d
)
于 2013-01-10T21:41:56.577 に答える
0

私はこれに対するいくつかの解決策をブログに投稿しました。私が取った最新のアプローチは次のとおりです

    function dec2fracso($dec){
    //Negative number flag.
    $num=$dec;
    if($num<0){
        $neg=true;
    }else{
        $neg=false;
    }

    //Extracts 2 strings from input number
    $decarr=explode('.',(string)$dec);

    //Checks for divided by zero input.
    if($decarr[1]==0){
        $decarr[1]=1;
        $fraccion[0]=$decarr[0];
        $fraccion[1]=$decarr[1];
        return $fraccion;
    }

    //Calculates the divisor before simplification.
    $long=strlen($decarr[1]);
    $div="1";
    for($x=0;$x<$long;$x++){
        $div.="0";
    }

    //Gets the greatest common divisor.
    $x=(int)$decarr[1];
    $y=(int)$div;
    $gcd=gmp_strval(gmp_gcd($x,$y));

    //Calculates the result and fills the array with the correct sign.
    if($neg){
        $fraccion[0]=((abs($decarr[0])*($y/$gcd))+($x/$gcd))*(-1);
    }else{
        $fraccion[0]=(abs($decarr[0])*($y/$gcd))+($x/$gcd);
    }
    $fraccion[1]=($y/$gcd);
    return $fraccion;
}
于 2014-03-25T23:37:31.273 に答える
0

フロートは十分に正確ではないため、深刻な問題に直面する必要があります。

に対処する必要がある場合1.3333、PHP はこの値を推定します...そのため、1 1/3.

克服するのは簡単に思えますが、プログラムで1/7901( ~ 1,2656625743576762435134793064169e-4) と1/7907( ~ 1,2647021626406981155937776653598e-4) を正確に区別したい場合は、これは本当に地獄です !!

私見、数学を扱いたい場合は、外部ライブラリに依存する必要があります...またはPHPがMatlabと通信するようにしてください。

詳細を知りたい場合は、浮動小数点の問題を掘り下げることをお勧めします... wikipediaから始めます。

于 2009-12-23T17:41:20.833 に答える
0

小数から小数への分数は非常に簡単で、多くの解決策があります。文字列をトリミングし、スペースを「+」に置き換え、スペース以外のものを置き換えます,/,. または '' を含む数字を '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;
   }
  }    
 }
}

しかし、個人的には、元の表現をデータベースの別のフィールドに保存するだけです。

于 2009-12-23T17:52:15.447 に答える
0

アプローチは、10 進数値を取得し、整数になるまで 2、3、4 などで乗算することです。

ただし、デレクの答えに固執します。n を高くして n/(n+1) を挿入するとどうなるか想像してみてください。このようなアルゴリズムでは、n+1 までのすべての数値をスキャンする必要があります。言うまでもなく、近似の問題が発生する可能性があります。

于 2009-12-23T17:09:53.223 に答える