PHP fraksiyonu ve arkasına ondalık dönüştürebilirsiniz?

10 Cevap php

Ben kullanıcı gibi bir kısmını yazmanız mümkün istiyorum:

 1/2
 2 1/4
 3

Ve MySQL, ben onun tarafından sipariş ve buna diğer karşılaştırmalar yapabilirsiniz bu şekilde kaydedilmiş olması, karşılık gelen ondalık dönüştürmek.

Kullanıcıya gösteren Ama ben geri kesir ondalık dönüştürmek gerekiyor

yani temelde ondalık kesir dize dönüştürmek bir işlev gerekir:

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

ve bir hizip dizeye bir ondalık dönüştürebilirsiniz bir fonksiyon:

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

Bunu nasıl yapabilirim?

10 Cevap

Ben matematik çalıştırmak kez, onu geri almak değil, gibi, ben de dize temsilini saklamak istiyorum düşünüyorum!

Ve, burada hızlı bir-n-kirli işlem fonksiyonu, hiçbir garanti bulunuyor:

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

Oh, bütünlüğü için, emin $fraction['denominator'] != 0 yapmak için bir onay ekleyebilirsiniz.

some ihtiyaçlarınız için PEAR'ın Math_Fraction sınıfını kullanabilirsiniz için

<?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();

?>

Bazen bunu yapmak ve yuvarlama kabul edilebilir bir yol bulmalıyız. Sizin için dışarı çalışır yuvarlaklaşma aralığı ne karar Yani eğer böyle bir işlevi inşa edebilirsiniz. Onu en yakın eşleşen olduğunu kesir bir ondalık dönüştürmek. Eğer test edilecek daha payda ekleyerek doğruluğunu uzanabilir.

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

Sen yüzer yeterince kesin değildir, çünkü ciddi bir sorun yüzleşmek zorunda olacak.

Eğer 1.3333, PHP bu değerin bir tahmin yapacak ile uğraşmak zorunda ... Yani olacak zaman 1 1/3 bunu dönüştürmek mümkün olmayacak.

It seems to be simple to overcome, but if you want your program to differentiate 1/7901 (~ 1,2656625743576762435134793064169e-4) with 1/7907 (~ 1,2647021626406981155937776653598e-4) precisely... this will be a real hell !!

Eğer matematik ile uğraşmak istiyorsanız IMHO, harici bir kütüphaneye güveniyor ... veya PHP Matlab ile iletişim yapmak için çalışmalısınız.

Eğer daha fazla bilmek istiyorsanız, ben size kayan nokta sorunları kazmak ... wikipedia ile başlayan öneririz.

Paydalar sadece sınırlı bir miktarda kullanılması durumunda Jir yaklaşımının değişimi aslında işe yarayabilir: (yakınlaştırılmasına bağlı kalan ondalık atmak ve sonuç yuvarlak) en küçük ortak paydalar her şeyi çarpın.

Yani: Eğer sadece halfs, thrids ve çeyrek ile uğraşmak zorunda varsa, sadece 12 ile her şeyi çarpın.

Eğer ortak payda biliyorsanız Ve ayrıca, bu büyük arama yerine mümkün olan bütün n 1 arama tam olarak hangi numaralar bilerek arama hızını azaltmak gerekir.

Vb 1/7 gibi sıradışı fraksiyonların sürü, 1/13, uğraşmak varsa iyi, Derek çözümü sadık ve çok özgün değerini saklayın.

Burada ilk olarak geçerli bir fraksiyon (ille basit fraksiyon rağmen) belirleyen bir çözümdür. Yani 0.05 -> 5/100. Daha sonra basit kesir, 1/20 aşağı düşürmek için pay ve payda en büyük ortak böleni belirler.

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

Eğer eminseniz gmp modülü takılı olmasına rağmen bu OBEB saf bir PHP uygulaması içerir sen olabilir use the one that comes with gcd.

Diğerleri de belirtildiği gibi rasyonel sayılar kullanmak gerekir. Yani bir ondalık 7/1 dönüştürmek ise daha sonra kaybedilen hassas geri 1/7 ulaşmasını engellemek olacaktır, çünkü geri şans olacak bir ondalık dönüştürmek için deneyin. Ben (standart ölçümler) ile uğraşıyorum tüm sayılar zaten rasyonel sayılar olduğundan benim amaçlar için bu kabul edilebilir.

Bir yaklaşım, bir tamsayı elde edene kadar üzerinde ondalık değerini almak ve 2 ile çarpın, 3, 4 ve olacaktır.

However, I'd stick with the answer given by Derek. Guess what happens when a user inserts n/(n+1) with n high. Such an algorithm would have to scan all the numbers up to n+1. Not to mention it is likely you'll end up with approximation problems.

Ondalık kesir oldukça basittir ve çözümleri çok sayıda bulunmaktadır. Ben '+' ile boşluk yerine, dize kırpma ile gitmek, ve uzayda başka bir şey, /, istiyorum. veya'' ile rakam daha sonra 'eval' yoluyla çalıştırarak.

Kesir ondalık doğru yapmak neredeyse imkansız - Eğer hassas bir sürü gevşek hangi noktadan - ondalık kesir muhtemelen ikili ilk olarak dönüştürülmesi gerekir az değil çünkü. Eğer 20976/41953 ve 1/2 arasındaki farkı ile yaşamak durumunda akademik bir egzersiz olarak ..... o zaman kesirler, önceden tanımlanmış bir dizi için bir bulanık eşleşme deneyebilirsiniz:

(- Ama okuyucu için bir alıştırma olarak bırakacağım orada aynı algoritmayı uygulayan bir kıvrımlara yolu muhtemelen).

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

Ama kişisel olarak, ben sadece veritabanında ayrı alanda orijinal temsil saklamak istiyorum.

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

Daha sonra $f == $n / $d

Örneğin:

print_r(dec2frac(3.1415926));

Çıkışlar:

Array
(
    [0] => 3537118815677477  // $n
    [1] => 1125899906842624  // $d
)

Arkadaşlar, bu yardımcı olabilir?

[] S


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

        return "1/{$denominator}";
    }
    else {
        return $number;
    }
}