it-swarm-tr.com

Yüzenleri insan tarafından okunabilen kesirlere dönüştürmek nasıl?

Diyelim ki 0.33'ümüz var, "1/3" çıkmamız gerekiyor. 
"0.4" varsa, "2/5" yazmamız gerekir.

Buradaki amaç, kullanıcının veriyi daha iyi anlamanın bir yolu olarak “y'nin x bölümünü” anlamasını sağlamak için insan tarafından okunabilir hale getirmektir.

Yüzdelerin iyi bir alternatif olduğunu biliyorum ama bunu yapmanın basit bir yolu olup olmadığını merak ediyordum?

97
Swaroop C H

David Eppstein'ın verilen gerçek sayıya C koduna rasyonel bir yaklaşım bulduğunu ve tam olarak ne istediğinizi buldum. Sürekli kesirler teorisi ve çok hızlı ve oldukça kompakt.

Bu özelleştirilmiş sürümlerini belirli pay ve payda limitleri için kullandım.

/*
** find rational approximation to given real number
** David Eppstein / UC Irvine / 8 Aug 1993
**
** With corrections from Arno Formella, May 2008
**
** usage: a.out r d
**   r is real number to approx
**   d is the maximum denominator allowed
**
** based on the theory of continued fractions
** if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
** then best approximation is found by truncating this series
** (with some adjustments in the last term).
**
** Note the fraction can be recovered as the first column of the matrix
**  ( a1 1 ) ( a2 1 ) ( a3 1 ) ...
**  ( 1  0 ) ( 1  0 ) ( 1  0 )
** Instead of keeping the sequence of continued fraction terms,
** we just keep the last partial product of these matrices.
*/

#include <stdio.h>

main(ac, av)
int ac;
char ** av;
{
    double atof();
    int atoi();
    void exit();

    long m[2][2];
    double x, startx;
    long maxden;
    long ai;

    /* read command line arguments */
    if (ac != 3) {
        fprintf(stderr, "usage: %s r d\n",av[0]);  // AF: argument missing
        exit(1);
    }
    startx = x = atof(av[1]);
    maxden = atoi(av[2]);

    /* initialize matrix */
    m[0][0] = m[1][1] = 1;
    m[0][1] = m[1][0] = 0;

    /* loop finding terms until denom gets too big */
    while (m[1][0] *  ( ai = (long)x ) + m[1][1] <= maxden) {
        long t;
        t = m[0][0] * ai + m[0][1];
        m[0][1] = m[0][0];
        m[0][0] = t;
        t = m[1][0] * ai + m[1][1];
        m[1][1] = m[1][0];
        m[1][0] = t;
        if(x==(double)ai) break;     // AF: division by zero
        x = 1/(x - (double) ai);
        if(x>(double)0x7FFFFFFF) break;  // AF: representation failure
    } 

    /* now remaining x is between 0 and 1/ai */
    /* approx as either 0 or 1/m where m is max that will fit in maxden */
    /* first try zero */
    printf("%ld/%ld, error = %e\n", m[0][0], m[1][0],
           startx - ((double) m[0][0] / (double) m[1][0]));

    /* now try other possibility */
    ai = (maxden - m[1][1]) / m[1][0];
    m[0][0] = m[0][0] * ai + m[0][1];
    m[1][0] = m[1][0] * ai + m[1][1];
    printf("%ld/%ld, error = %e\n", m[0][0], m[1][0],
           startx - ((double) m[0][0] / (double) m[1][0]));
}
66
Epsilon

Python 2.6'dan itibaren fractions / modülü var.

(Dokümanlardan alıntı yapmak.)

>>> from fractions import Fraction
>>> Fraction('3.1415926535897932').limit_denominator(1000)
Fraction(355, 113)

>>> from math import pi, cos
>>> Fraction.from_float(cos(pi/3))
Fraction(4503599627370497, 9007199254740992)
>>> Fraction.from_float(cos(pi/3)).limit_denominator()
Fraction(1, 2)
25
Debilski

Çıktı, bir insan okuyucusuna sonucun sırasına hızlı bir etki bırakmaksa, "113/211" gibi bir şey döndürmenin hiçbir anlamı yoktur, bu nedenle çıktı tek basamaklı sayıları (ve belki 1/1 sayılarını kullanmakla sınırlandırmalıdır). 10 ve 9/10). Eğer öyleyse, sadece 27 farklı kesirleri olduğunu gözlemleyebilirsiniz.

Çıktının üretilmesi için altta yatan matematik asla değişmeyeceğinden, bir çözüm ikili bir arama ağacını basit bir şekilde kodlamak olabilir, böylece işlev en fazla log (27) ~ = 4 3/4 karşılaştırması yapar. İşte kodun test edilmiş bir C versiyonu

char *userTextForDouble(double d, char *rval)
{
    if (d == 0.0)
        return "0";

    // TODO: negative numbers:if (d < 0.0)...
    if (d >= 1.0)
        sprintf(rval, "%.0f ", floor(d));
    d = d-floor(d); // now only the fractional part is left

    if (d == 0.0)
        return rval;

    if( d < 0.47 )
    {
        if( d < 0.25 )
        {
            if( d < 0.16 )
            {
                if( d < 0.12 ) // Note: fixed from .13
                {
                    if( d < 0.11 )
                        strcat(rval, "1/10"); // .1
                    else
                        strcat(rval, "1/9"); // .1111....
                }
                else // d >= .12
                {
                    if( d < 0.14 )
                        strcat(rval, "1/8"); // .125
                    else
                        strcat(rval, "1/7"); // .1428...
                }
            }
            else // d >= .16
            {
                if( d < 0.19 )
                {
                    strcat(rval, "1/6"); // .1666...
                }
                else // d > .19
                {
                    if( d < 0.22 )
                        strcat(rval, "1/5"); // .2
                    else
                        strcat(rval, "2/9"); // .2222...
                }
            }
        }
        else // d >= .25
        {
            if( d < 0.37 ) // Note: fixed from .38
            {
                if( d < 0.28 ) // Note: fixed from .29
                {
                    strcat(rval, "1/4"); // .25
                }
                else // d >=.28
                {
                    if( d < 0.31 )
                        strcat(rval, "2/7"); // .2857...
                    else
                        strcat(rval, "1/3"); // .3333...
                }
            }
            else // d >= .37
            {
                if( d < 0.42 ) // Note: fixed from .43
                {
                    if( d < 0.40 )
                        strcat(rval, "3/8"); // .375
                    else
                        strcat(rval, "2/5"); // .4
                }
                else // d >= .42
                {
                    if( d < 0.44 )
                        strcat(rval, "3/7"); // .4285...
                    else
                        strcat(rval, "4/9"); // .4444...
                }
            }
        }
    }
    else
    {
        if( d < 0.71 )
        {
            if( d < 0.60 )
            {
                if( d < 0.55 ) // Note: fixed from .56
                {
                    strcat(rval, "1/2"); // .5
                }
                else // d >= .55
                {
                    if( d < 0.57 )
                        strcat(rval, "5/9"); // .5555...
                    else
                        strcat(rval, "4/7"); // .5714
                }
            }
            else // d >= .6
            {
                if( d < 0.62 ) // Note: Fixed from .63
                {
                    strcat(rval, "3/5"); // .6
                }
                else // d >= .62
                {
                    if( d < 0.66 )
                        strcat(rval, "5/8"); // .625
                    else
                        strcat(rval, "2/3"); // .6666...
                }
            }
        }
        else
        {
            if( d < 0.80 )
            {
                if( d < 0.74 )
                {
                    strcat(rval, "5/7"); // .7142...
                }
                else // d >= .74
                {
                    if(d < 0.77 ) // Note: fixed from .78
                        strcat(rval, "3/4"); // .75
                    else
                        strcat(rval, "7/9"); // .7777...
                }
            }
            else // d >= .8
            {
                if( d < 0.85 ) // Note: fixed from .86
                {
                    if( d < 0.83 )
                        strcat(rval, "4/5"); // .8
                    else
                        strcat(rval, "5/6"); // .8333...
                }
                else // d >= .85
                {
                    if( d < 0.87 ) // Note: fixed from .88
                    {
                        strcat(rval, "6/7"); // .8571
                    }
                    else // d >= .87
                    {
                        if( d < 0.88 ) // Note: fixed from .89
                        {
                            strcat(rval, "7/8"); // .875
                        }
                        else // d >= .88
                        {
                            if( d < 0.90 )
                                strcat(rval, "8/9"); // .8888...
                            else
                                strcat(rval, "9/10"); // .9
                        }
                    }
                }
            }
        }
    }

    return rval;
}
21
J P

Ondalık kesirleri kesir haline getirmenin ardındaki matematiği açıklayan bir link:

http://www.webmath.com/dec2fract.html

Ve işte VB kullanarak gerçekte nasıl yapılacağına ilişkin örnek bir işlev (www.freevbcode.com/ShowCode.asp?ID=582 adresinden):

Public Function Dec2Frac(ByVal f As Double) As String

   Dim df As Double
   Dim lUpperPart As Long
   Dim lLowerPart As Long

   lUpperPart = 1
   lLowerPart = 1

   df = lUpperPart / lLowerPart
   While (df <> f)
      If (df < f) Then
         lUpperPart = lUpperPart + 1
      Else
         lLowerPart = lLowerPart + 1
         lUpperPart = f * lLowerPart
      End If
      df = lUpperPart / lLowerPart
   Wend
Dec2Frac = CStr(lUpperPart) & "/" & CStr(lLowerPart)
End Function

(Google aramalardan: ondalıktan kesireye, ondalık kesirden koda dönüştür)

16
devinmoore

Her Bilgisayar Bilim Adamının Kayan Nokta Aritmetiği Hakkında Bilmeniz Gerekenler .

Çok fazla sayıda çarparak bir hassasiyet belirtmeniz gerekir:

3.141592 * 1000000 = 3141592

o zaman bir kesir yapabilirsiniz:

3 + (141592 / 1000000)

ve GCD ile azaltmak ...

3 + (17699 / 125000)

ancak amaçlanan kesirini çıkarmanın bir yolu yoktur. Kodunuz boyunca always kullanarak kesirleri kullanmak isteyebilirsiniz - taşmayı önlemek için mümkün olduğunda kesirleri azaltmayı unutmayın!

9
nlucaroni

Devinmoore tarafından önerilen VB kodunun Perl ve Javascript sürümleri:

Perl:

sub dec2frac {
    my $d = shift;

    my $df  = 1;
    my $top = 1;
    my $bot = 1;

    while ($df != $d) {
      if ($df < $d) {
        $top += 1;
      }
      else {
         $bot += 1;
         $top = int($d * $bot);
      }
      $df = $top / $bot;
   }
   return "$top/$bot";
}

Ve neredeyse aynı javascript:

function dec2frac(d) {

    var df = 1;
    var top = 1;
    var bot = 1;

    while (df != d) {
        if (df < d) {
            top += 1;
        }
        else {
            bot += 1;
            top = parseInt(d * bot);
        }
        df = top / bot;
    }
    return top + '/' + bot;
}
9
mivk

Bir C # uygulaması

/// <summary>
/// Represents a rational number
/// </summary>
public struct Fraction
{
    public int Numerator;
    public int Denominator;

    /// <summary>
    /// Constructor
    /// </summary>
    public Fraction(int numerator, int denominator)
    {
        this.Numerator = numerator;
        this.Denominator = denominator;
    }

    /// <summary>
    /// Approximates a fraction from the provided double
    /// </summary>
    public static Fraction Parse(double d)
    {
        return ApproximateFraction(d);
    }

    /// <summary>
    /// Returns this fraction expressed as a double, rounded to the specified number of decimal places.
    /// Returns double.NaN if denominator is zero
    /// </summary>
    public double ToDouble(int decimalPlaces)
    {
        if (this.Denominator == 0)
            return double.NaN;

        return System.Math.Round(
            Numerator / (double)Denominator,
            decimalPlaces
        );
    }


    /// <summary>
    /// Approximates the provided value to a fraction.
    /// http://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions
    /// </summary>
    private static Fraction ApproximateFraction(double value)
    {
        const double EPSILON = .000001d;

        int n = 1;  // numerator
        int d = 1;  // denominator
        double fraction = n / d;

        while (System.Math.Abs(fraction - value) > EPSILON)
        {
            if (fraction < value)
            {
                n++;
            }
            else
            {
                d++;
                n = (int)System.Math.Round(value * d);
            }

            fraction = n / (double)d;
        }

        return new Fraction(n, d);
    }
}
9
Tom

Stern-Brocot Tree / basit paydalı kesirler ile gerçek sayıları yaklaşık olarak hesaplamak için oldukça doğal bir yol başlatır.

7
Doug McClean

Sorunun bir kısmı, pek çok kesimin aslında kesir olarak kolayca anlaşılmamasıdır. Örneğin. 0.33 1/3 değil, 33/100. Ancak, ilkokul eğitiminizi hatırlarsanız, o zaman ondalık değerleri kesirlere dönüştürme işlemi vardır, ancak çoğu zaman ondalık sayılar 0.33'te değil, 0.32999999999999998 ya da bazılarında saklanmadığından, ne istediğinizi vermek pek mümkün değildir.

Kendinize bir iyilik yapın ve bununla uğraşmayın, ancak gerekirse, aşağıdakileri yapabilirsiniz:

Kesirli parçayı kaldırana kadar orijinal değeri 10 ile çarpın. Bu numarayı saklayın ve bölen olarak kullanın. Sonra ortak paydaları arayarak bir dizi basitleştirme yapın.

0,4, 4/10 olur. Daha sonra düşük değerli, muhtemelen asal sayılarla başlayan ortak bölenleri ararsınız. 2 ile başlayarak, 2'nin bölmenin tabanının bölmenin kendisiyle aynı olup olmadığını kontrol ederek hem pay hem de paydayı eşit olarak bölüp ayırmadığını görürsünüz.

floor(5/2) = 2
5/2 = 2.5

Yani 5 eşit 2 bölülemez. Sonra bir sonraki sayıyı kontrol edin, 3 söyleyin. Bunu daha küçük sayının kareköküne veya üstüne gelene kadar yapın.

Bunu yaptıktan sonra ihtiyacın var.

5
Orion Adrian

Bu bir "algoritma" değil, sadece bir Python çözümü değil: http://docs.python.org/library/fractions.html

>>> from fractions import Fraction
>>> Fraction('3.1415926535897932').limit_denominator(1000)
Fraction(355, 113)
5
eldad

"Diyelim ki 0.33'ümüz var," 1/3 "çıkmamız gerekiyor."

"Çözümün" ne kadar hassas olmasını bekliyorsunuz? 0.33 1/3'e eşit değildir. "İyi" (okunması kolay) bir cevabı nasıl tanırsınız?

Ne olursa olsun, olası bir algoritma olabilir:

Y'nin 10'dan daha az olduğu X/Y formunda en yakın kesimi bulmayı düşünüyorsanız, her Y hesaplama X için mümkün olan 9 Y'nin tümünü de doldurabilir ve en doğru olanı seçebilirsiniz.

4
Suma

R'de yerleşik bir çözüm:

library(MASS)
fractions(0.666666666)
## [1] 2/3

Bu sürekli bir kesir yöntemi kullanır ve hassasiyeti ayarlamak için isteğe bağlı cycles ve max.denominator argümanlarına sahiptir.

3
Ben Bolker

Bence bunu yapmanın en iyi yolu ilk önce float değerinizi ascii temsiline dönüştürmektir. C++ 'ta ostringstream veya C' de kullanabilirsiniz, sprintf kullanabilirsiniz. İşte C++ 'ta nasıl görüneceği:

ostringstream oss;
float num;
cin >> num;
oss << num;
string numStr = oss.str();
int i = numStr.length(), pow_ten = 0;
while (i > 0) {
    if (numStr[i] == '.')
        break;
    pow_ten++;
    i--;
}
for (int j = 1; j < pow_ten; j++) {
    num *= 10.0;
}
cout << static_cast<int>(num) << "/" << pow(10, pow_ten - 1) << endl;

Benzer bir yaklaşım düz C de alınabilir.

Daha sonra kesirin en düşük düzeyde olduğunu kontrol etmeniz gerekir. Bu algoritma kesin bir cevap verecektir, yani 0.33, "1/3" değil, "33/100" çıktısını verecektir. Bununla birlikte, 0.4 "4/10" verecektir, bu en düşük sürelere indirildiğinde "2/5" olacaktır. Bu EppStein'ın çözümü kadar güçlü olmayabilir, ama bunun daha kolay olduğuna inanıyorum. 

2
bpm

Bir çözüm, tüm sayıları ilk etapta rasyonel sayılar olarak kaydetmektir. Rasyonel sayı aritmetiği için kütüphaneler var (örneğin GMP ). OO dilini kullanıyorsanız, sayı sınıfınızı değiştirmek için sadece rasyonel bir sayı sınıfı kütüphanesini kullanabilirsiniz.

Finans programları, diğerleri arasında, kesin hesaplamalar yapabilmek ve düz bir şamandıra kullanarak kaybolabilecek hassasiyeti korumak için böyle bir çözüm kullanır.

Elbette çok daha yavaş olacaktır, bu yüzden sizin için pratik olmayabilir. Yapmanız gereken hesaplama miktarına ve hassasiyetin sizin için ne kadar önemli olduğuna bağlı olarak değişir.

a = rational(1);
b = rational(3);
c = a / b;

print (c.asFraction)  --->  "1/3"
print (c.asFloat) ----> "0.333333"
2
robottobor

Sınırsız boyutlu tamsayıları saklayabilen bir 'BigInt' sınıfınız olduğunu varsayarak C++ 'a cevap verin.

Bunun yerine 'imzasız uzun süre' kullanabilirsiniz, ancak yalnızca belirli değerler için çalışacaktır.

void GetRational(double val)
{
    if (val == val+1) // Inf
        throw "Infinite Value";
    if (val != val) // NaN
        throw "Undefined Value";

    bool sign = false;
    BigInt enumerator = 0;
    BigInt denominator = 1;

    if (val < 0)
    {
        val = -val;
        sign = true;
    }

    while (val > 0)
    {
        unsigned int intVal = (unsigned int)val;
        val -= intVal;
        enumerator += intVal;
        val *= 2;
        enumerator *= 2;
        denominator *= 2;
    }

    BigInt gcd = GCD(enumerator,denominator);
    enumerator /= gcd;
    denominator /= gcd;

    Print(sign? "-":"+");
    Print(enumerator);
    Print("/");
    Print(denominator);

    // Or simply return {sign,enumerator,denominator} as you wish
}

BTW, GetRational (0.0) "+0/1" değerini döndürür, bu nedenle ayrı ayrı ele almak isteyebilirsiniz.

P.S .: Bu kodu birkaç senedir kendi 'RationalNum' sınıfında kullanıyorum ve tamamen test edildi.

2
barak manos

Hangi seviyede hata kabul etmek istediğinizi çözmeniz gerekecektir. Ondalık kesirlerin tümü basit bir kesriye indirgenmez. Muhtemelen 60 gibi kolayca bölünebilen bir sayı seçer ve değere en fazla 60'ın kaç olduğunu bulur, sonra kesriyi basitleştirirdim.

2
Mark Bessey

Bu algoritma, Ian RichardsJOHN KENNEDY TARAFINDAN YALNIZCA NICE KESIRLERINI D&OUML;ND&UUML;RMEKLE KALMAZ, AYNI ZAMANDA HIZ A&CCEDIL;ISINDAN DA &CCEDIL;OK IYI PERFORMANS G&OUML;STERIR. BU, BU CEVAP TARAFINDAN ALINAN _ KODUDUR.

GEREKIRSE EKLEMEK ZORUNDA KALACAĞINIZ NAN VE +/- SONSUZLUK GIBI &OUML;ZEL DEĞERLER HARI&CCEDIL; T&UUML;M double DEĞERLERINI IŞLEYEBILIR.

new Fraction(numerator, denominator) IŞLEVINI D&OUML;ND&UUML;R&UUML;R. KENDI T&UUML;R&UUML;N&UUML;ZLE DEĞIŞTIRIN.

_/DAHA FAZLA &OUML;RNEK DEĞER VE DIĞER ALGORITMALARLA KARŞILAŞTIRMA I&CCEDIL;IN BURAYA GIDIN

public Fraction RealToFraction(double value, double accuracy)
{
    if (accuracy <= 0.0 || accuracy >= 1.0)
    {
        throw new ArgumentOutOfRangeException("accuracy", "Must be > 0 and < 1.");
    }

    int sign = Math.Sign(value);

    if (sign == -1)
    {
        value = Math.Abs(value);
    }

    // Accuracy is the maximum relative error; convert to absolute maxError
    double maxError = sign == 0 ? accuracy : value * accuracy;

    int n = (int) Math.Floor(value);
    value -= n;

    if (value < maxError)
    {
        return new Fraction(sign * n, 1);
    }

    if (1 - maxError < value)
    {
        return new Fraction(sign * (n + 1), 1);
    }

    double z = value;
    int previousDenominator = 0;
    int denominator = 1;
    int numerator;

    do
    {
        z = 1.0 / (z - (int) z);
        int temp = denominator;
        denominator = denominator * (int) z + previousDenominator;
        previousDenominator = temp;
        numerator = Convert.ToInt32(value * denominator);
    }
    while (Math.Abs(value - (double) numerator / denominator) > maxError && z != (int) z);

    return new Fraction((n * denominator + numerator) * sign, denominator);
}

Bu algoritma tarafından döndürülen örnek değerler:

Accuracy: 1.0E-3      | Richards                     
Input                 | Result           Error       
======================| =============================
   3                  |       3/1          0         
   0.999999           |       1/1         1.0E-6     
   1.000001           |       1/1        -1.0E-6     
   0.50 (1/2)         |       1/2          0         
   0.33... (1/3)      |       1/3          0         
   0.67... (2/3)      |       2/3          0         
   0.25 (1/4)         |       1/4          0         
   0.11... (1/9)      |       1/9          0         
   0.09... (1/11)     |       1/11         0         
   0.62... (307/499)  |       8/13        2.5E-4     
   0.14... (33/229)   |      16/111       2.7E-4     
   0.05... (33/683)   |      10/207      -1.5E-4     
   0.18... (100/541)  |      17/92       -3.3E-4     
   0.06... (33/541)   |       5/82       -3.7E-4     
   0.1                |       1/10         0         
   0.2                |       1/5          0         
   0.3                |       3/10         0         
   0.4                |       2/5          0         
   0.5                |       1/2          0         
   0.6                |       3/5          0         
   0.7                |       7/10         0         
   0.8                |       4/5          0         
   0.9                |       9/10         0         
   0.01               |       1/100        0         
   0.001              |       1/1000       0         
   0.0001             |       1/10000      0         
   0.33333333333      |       1/3         1.0E-11    
   0.333              |     333/1000       0         
   0.7777             |       7/9         1.0E-4     
   0.11               |      10/91       -1.0E-3     
   0.1111             |       1/9         1.0E-4     
   3.14               |      22/7         9.1E-4     
   3.14... (pi)       |      22/7         4.0E-4     
   2.72... (e)        |      87/32        1.7E-4     
   0.7454545454545    |      38/51       -4.8E-4     
   0.01024801004      |       2/195       8.2E-4     
   0.99011            |     100/101      -1.1E-5     
   0.26... (5/19)     |       5/19         0         
   0.61... (37/61)    |      17/28        9.7E-4     
                      | 
Accuracy: 1.0E-4      | Richards                     
Input                 | Result           Error       
======================| =============================
   0.62... (307/499)  |     299/486      -6.7E-6     
   0.05... (33/683)   |      23/476       6.4E-5     
   0.06... (33/541)   |      33/541        0         
   1E-05              |       1/99999     1.0E-5     
   0.7777             |    1109/1426     -1.8E-7     
   3.14... (pi)       |     333/106      -2.6E-5     
   2.72... (e)        |     193/71        1.0E-5     
   0.61... (37/61)    |      37/61         0         
2
Kay Zed

Bunu, aşağıdaki adımları kullanarak herhangi bir programlama dilinde yapabilirsiniz:

  1. Çarpın ve 10 ^ x ile Böl, burada x, sayının kalan hiçbir yeri olmadığından emin olmak için 10 değerinin gücüdür. Örnek: 33'ü yapmak için 0.33'ü 10 ^ 2 = 100 ile çarpın ve 33/100 elde etmek için aynı şekilde bölün.
  2. Sonuçtan tamsayıları elde edene kadar, elde edilen kesir payını ve paydasını çarpanlara ayırma ile azaltın.
  3. Ortaya çıkan azaltılmış kesir, cevabınız olmalıdır.

Örnek: 0.2 = 0.2 x 10 ^ 1/10 ^ 1 = 2/10 = 1/5

Böylece, '5 üzerinden 1 kısım' olarak okunabilir.

2
Pascal

Ruby'nin yerleşik bir çözümü zaten var:

0.33.rationalize.to_s # => "33/100"
0.4.rationalize.to_s # => "2/5"

Rails'de, ActiveRecord sayısal nitelikleri de dönüştürülebilir:

product.size = 0.33
product.size.to_r.to_s # => "33/100"
2
Josh W Lewis

Yukarıdaki kodu tamamladı ve as3'e çevirdi

public static function toFrac(f:Number) : String
    {
        if (f>1)
        {
            var parte1:int;
            var parte2:Number;
            var resultado:String;
            var loc:int = String(f).indexOf(".");
            parte2 = Number(String(f).slice(loc, String(f).length));
            parte1 = int(String(f).slice(0,loc));
            resultado = toFrac(parte2);
            parte1 *= int(resultado.slice(resultado.indexOf("/") + 1, resultado.length)) + int(resultado.slice(0, resultado.indexOf("/")));
            resultado = String(parte1) +  resultado.slice(resultado.indexOf("/"), resultado.length)
            return resultado;
        }
        if( f < 0.47 )
            if( f < 0.25 )
                if( f < 0.16 )
                    if( f < 0.13 )
                        if( f < 0.11 )
                            return "1/10";
                        else
                            return "1/9";
                    else
                        if( f < 0.14 )
                            return "1/8";
                        else
                            return "1/7";
                else
                    if( f < 0.19 )
                        return "1/6";
                    else
                        if( f < 0.22 )
                            return "1/5";
                        else
                            return "2/9";
            else
                if( f < 0.38 )
                    if( f < 0.29 )
                        return "1/4";
                    else
                        if( f < 0.31 )
                            return "2/7";
                        else
                            return "1/3";
                else
                    if( f < 0.43 )
                        if( f < 0.40 )
                            return "3/8";
                        else
                            return "2/5";
                    else
                        if( f < 0.44 )
                            return "3/7";
                        else
                            return "4/9";
        else
            if( f < 0.71 )
                if( f < 0.60 )
                    if( f < 0.56 )
                        return "1/2";
                    else
                        if( f < 0.57 )
                            return "5/9";
                        else
                            return "4/7";
                else
                    if( f < 0.63 )
                        return "3/5";
                    else
                        if( f < 0.66 )
                            return "5/8";
                        else
                            return "2/3";
            else
                if( f < 0.80 )
                    if( f < 0.74 )
                        return "5/7";
                    else
                        if(f < 0.78 )
                            return "3/4";
                        else
                            return "7/9";
                else
                    if( f < 0.86 )
                        if( f < 0.83 )
                            return "4/5";
                        else
                            return "5/6";
                    else
                        if( f < 0.88 )
                            return "6/7";
                        else
                            if( f < 0.89 )
                                return "7/8";
                            else
                                if( f < 0.90 )
                                    return "8/9";
                                else
                                    return "9/10";
    }
1
João Lopes

Javascript'te kaba kuvvet yaklaşımı kullanan hızlı ve kirli bir uygulama. Hiç de optimize edilmemiş, önceden tanımlanmış bir kesir aralığında çalışıyor: http://jsfiddle.net/PdL23/1/

/* This should convert any decimals to a simplified fraction within the range specified by the two for loops. Haven't done any thorough testing, but it seems to work fine.

I have set the bounds for numerator and denominator to 20, 20... but you can increase this if you want in the two for loops.

Disclaimer: Its not at all optimized. (Feel free to create an improved version.)
*/

decimalToSimplifiedFraction = function(n) {

    for(num = 1; num < 20; num++) {  // "num" is the potential numerator
        for(den = 1; den < 20; den++) {  // "den" is the potential denominator
            var multiplyByInverse = (n * den ) / num;

            var roundingError = Math.round(multiplyByInverse) - multiplyByInverse;

            // Checking if we have found the inverse of the number, 
            if((Math.round(multiplyByInverse) == 1) && (Math.abs(roundingError) < 0.01)) {
                return num + "/" + den;
            }
        }
    }
};

//Put in your test number here.
var floatNumber = 2.56;

alert(floatNumber + " = " + decimalToSimplifiedFraction(floatNumber));

Bu JPS tarafından kullanılan yaklaşımdan ilham almıştır.

1
Deepak Joy

Bunu zorlaştıracak iki temel probleminiz olacak:

1) Kayan nokta, "z" değerine neden olan bir "x/y" kesirine sahipseniz, kesir algoritmanızın "x/y" dışında bir sonuç verebileceği anlamına gelen kesin bir gösterim değildir.

2) Sonsuzluk rasyonelden çok daha fazla irrasyonel sayı vardır. Rasyonel bir sayı, kesir olarak gösterilebilen sayıdır. Mantıksız olanlar olamaz.

Bununla birlikte, ucuz bir şekilde, kayan nokta limitin kesinliğine sahip olduğu için, onu her zaman bir fraksiyon şekli olarak temsil edebilirsiniz. (Bence...)

1
Torlack

Diyelim ki 0.33'ümüz var, "1/3" çıkmamız gerekiyor. Eğer "0.4" varsa, biz "2/5" çıkışı gerekiyor.

Genel durumda yanlış, 1/3 = 0.3333333 = 0 (0). (3) Ayrıca, yukarıda belirtilen çözümlerden bulmak imkansız, çıktı her zaman kesir çünkü tanımlanmış kesinlik ile kesriye dönüştürülebilir.

AMA, kapsamlı işlevimi, Sonsuz geometrik seri fikrine dayanarak, özellikle de aşağıdaki formüle dayanarak, birçok seçenekle öneririm:

enter image description here

İlk başta bu işlev dize gösteriminde kesir dönemini bulmaya çalışıyor. Bundan sonra yukarıda açıklanan formül uygulanır.

Rasyonel sayı kodu, C # 'daki Stephen M. McKamey rasyonel sayı uygulamasından ödünç alınmıştır. Umarım kodumu başka dillerde taşımak çok zor değildir.

/// <summary>
/// Convert decimal to fraction
/// </summary>
/// <param name="value">decimal value to convert</param>
/// <param name="result">result fraction if conversation is succsess</param>
/// <param name="decimalPlaces">precision of considereation frac part of value</param>
/// <param name="trimZeroes">trim zeroes on the right part of the value or not</param>
/// <param name="minPeriodRepeat">minimum period repeating</param>
/// <param name="digitsForReal">precision for determination value to real if period has not been founded</param>
/// <returns></returns>
public static bool FromDecimal(decimal value, out Rational<T> result, 
    int decimalPlaces = 28, bool trimZeroes = false, decimal minPeriodRepeat = 2, int digitsForReal = 9)
{
    var valueStr = value.ToString("0.0000000000000000000000000000", CultureInfo.InvariantCulture);
    var strs = valueStr.Split('.');

    long intPart = long.Parse(strs[0]);
    string fracPartTrimEnd = strs[1].TrimEnd(new char[] { '0' });
    string fracPart;

    if (trimZeroes)
    {
        fracPart = fracPartTrimEnd;
        decimalPlaces = Math.Min(decimalPlaces, fracPart.Length);
    }
    else
        fracPart = strs[1];

    result = new Rational<T>();
    try
    {
        string periodPart;
        bool periodFound = false;

        int i;
        for (i = 0; i < fracPart.Length; i++)
        {
            if (fracPart[i] == '0' && i != 0)
                continue;

            for (int j = i + 1; j < fracPart.Length; j++)
            {
                periodPart = fracPart.Substring(i, j - i);
                periodFound = true;
                decimal periodRepeat = 1;
                decimal periodStep = 1.0m / periodPart.Length;
                var upperBound = Math.Min(fracPart.Length, decimalPlaces);
                int k;
                for (k = i + periodPart.Length; k < upperBound; k += 1)
                {
                    if (periodPart[(k - i) % periodPart.Length] != fracPart[k])
                    {
                        periodFound = false;
                        break;
                    }
                    periodRepeat += periodStep;
                }

                if (!periodFound && upperBound - k <= periodPart.Length && periodPart[(upperBound - i) % periodPart.Length] > '5')
                {
                    var ind = (k - i) % periodPart.Length;
                    var regroupedPeriod = (periodPart.Substring(ind) + periodPart.Remove(ind)).Substring(0, upperBound - k);
                    ulong periodTailPlusOne = ulong.Parse(regroupedPeriod) + 1;
                    ulong fracTail = ulong.Parse(fracPart.Substring(k, regroupedPeriod.Length));
                    if (periodTailPlusOne == fracTail)
                        periodFound = true;
                }

                if (periodFound && periodRepeat >= minPeriodRepeat)
                {
                    result = FromDecimal(strs[0], fracPart.Substring(0, i), periodPart);
                    break;
                }
                else
                    periodFound = false;
            }

            if (periodFound)
                break;
        }

        if (!periodFound)
        {
            if (fracPartTrimEnd.Length >= digitsForReal)
                return false;
            else
            {
                result = new Rational<T>(long.Parse(strs[0]), 1, false);
                if (fracPartTrimEnd.Length != 0)
                    result = new Rational<T>(ulong.Parse(fracPartTrimEnd), TenInPower(fracPartTrimEnd.Length));
                return true;
            }
        }

        return true;
    }
    catch
    {
        return false;
    }
}

public static Rational<T> FromDecimal(string intPart, string fracPart, string periodPart)
{
    Rational<T> firstFracPart;
    if (fracPart != null && fracPart.Length != 0)
    {
        ulong denominator = TenInPower(fracPart.Length);
        firstFracPart = new Rational<T>(ulong.Parse(fracPart), denominator);
    }
    else
        firstFracPart = new Rational<T>(0, 1, false);

    Rational<T> secondFracPart;
    if (periodPart != null && periodPart.Length != 0)
        secondFracPart =
            new Rational<T>(ulong.Parse(periodPart), TenInPower(fracPart.Length)) *
            new Rational<T>(1, Nines((ulong)periodPart.Length), false);
    else
        secondFracPart = new Rational<T>(0, 1, false);

    var result = firstFracPart + secondFracPart;
    if (intPart != null && intPart.Length != 0)
    {
        long intPartLong = long.Parse(intPart);
        result = new Rational<T>(intPartLong, 1, false) + (intPartLong == 0 ? 1 : Math.Sign(intPartLong)) * result;
    }

    return result;
}

private static ulong TenInPower(int power)
{
    ulong result = 1;
    for (int l = 0; l < power; l++)
        result *= 10;
    return result;
}

private static decimal TenInNegPower(int power)
{
    decimal result = 1;
    for (int l = 0; l > power; l--)
        result /= 10.0m;
    return result;
}

private static ulong Nines(ulong power)
{
    ulong result = 9;
    if (power >= 0)
        for (ulong l = 0; l < power - 1; l++)
            result = result * 10 + 9;
    return result;
}

Bazı kullanım örnekleri vardır:

Rational<long>.FromDecimal(0.33333333m, out r, 8, false);
// then r == 1 / 3;

Rational<long>.FromDecimal(0.33333333m, out r, 9, false);
// then r == 33333333 / 100000000;

Sağ kısımda sıfır parça kesimiyle durumunuz:

Rational<long>.FromDecimal(0.33m, out r, 28, true);
// then r == 1 / 3;

Rational<long>.FromDecimal(0.33m, out r, 28, true);
// then r == 33 / 100;

Minimum süre gösterimi:

Rational<long>.FromDecimal(0.123412m, out r, 28, true, 1.5m));
// then r == 1234 / 9999;
Rational<long>.FromDecimal(0.123412m, out r, 28, true, 1.6m));
// then r == 123412 / 1000000; because of minimu repeating of period is 0.1234123 in this case.

Sonunda yuvarlama:

Rational<long>.FromDecimal(0.8888888888888888888888888889m, out r));
// then r == 8 == 9;

En ilginç vaka:

Rational<long>.FromDecimal(0.12345678m, out r, 28, true, 2, 9);
// then r == 12345678 / 100000000;

Rational<long>.FromDecimal(0.12345678m, out r, 28, true, 2, 8);
// Conversation failed, because of period has not been founded and there are too many digits in fraction part of input value.

Rational<long>.FromDecimal(0.12121212121212121m, out r, 28, true, 2, 9));
// then r == 4 / 33; Despite of too many digits in input value, period has been founded. Thus it's possible to convert value to fraction.

Diğer testler ve kod herkesin bulabileceği github'daki MathFunctions kütüphanem .

1
Ivan Kochurkin

Bir anamorfizmden faydalanan özellikle şık bir Haskell çözümü ile karşılaştım. özyineleme şemalarına pakete bağlıdır.

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleContexts    #-}

import           Control.Applicative   (liftA2)
import           Control.Monad         (ap)
import           Data.Functor.Foldable
import           Data.Ratio            (Ratio, (%))

isInteger :: (RealFrac a) => a -> Bool
isInteger = ((==) <*>) (realToFrac . floor)

continuedFraction :: (RealFrac a) => a -> [Int]
continuedFraction = liftA2 (:) floor (ana coalgebra)
    where coalgebra x
              | isInteger x = Nil
              | otherwise = Cons (floor alpha) alpha
                  where alpha = 1 / (x - realToFrac (floor x))

collapseFraction :: (Integral a) => [Int] -> Ratio a
collapseFraction [x]    = fromIntegral x % 1
collapseFraction (x:xs) = (fromIntegral x % 1) + 1 / collapseFraction xs

-- | Use the nth convergent to approximate x
approximate :: (RealFrac a, Integral b) => a -> Int -> Ratio b
approximate x n = collapseFraction $ take n (continuedFraction x)

Bunu ghci'de denerseniz, gerçekten işe yarıyor!

λ:> approximate pi 2
22 % 7
0
user8174234

Birçok insanın belirttiği gibi, kayan bir noktayı tekrar kesire dönüştüremezsiniz (.25 gibi bir şey değilse). Elbette, geniş bir kesir dizisi için bir tür arama yaratabilir ve aradığınız sonucu elde etmek için bir çeşit bulanık mantık kullanabilirsiniz. Yine de bu tam olarak doğru olmaz ve paydaşın ne kadar büyük olmasını istediğinize dair daha düşük bir sınır tanımlamanız gerekir.

.32 <x <.34 = 1/3 veya bunun gibi bir şey.

0
Tim

İşte Ruby için uygulama http://github.com/valodzka/frac

Math.frac(0.2, 100)  # => (1/5)
Math.frac(0.33, 10)  # => (1/3)
Math.frac(0.33, 100) # => (33/100)
0
valodzka