it-swarm-eu.dev

Jak převést plováky na lidské čitelné frakce?

Řekněme, že máme 0,33, musíme výstup "1/3". 
Pokud máme "0.4", musíme výstup "2/5".

Cílem je, aby byl člověk čitelný, aby uživatel pochopil "x části z y" jako lepší způsob porozumění datům.

Vím, že procenta jsou dobrá náhrada, ale zajímalo by mě, jestli to bylo jednoduché?

97
Swaroop C H

Zjistil jsem, že David Eppstein má racionální aproximaci k danému reálnému číslu C kód, který je přesně to, o co žádáte. Jeho základem je teorie pokračujících zlomků a velmi rychlá a poměrně kompaktní.

Použil jsem verze tohoto přizpůsobené pro konkrétní limity čitatele a jmenovatele.

/*
** 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

Z Pythonu 2.6 je zde modul fractions .

(Citace z dokumentů.)

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

Pokud je výstupem dát lidskému čtenáři rychlý dojem pořadí výsledku, nemá smysl vrátit něco jako "113/211", takže výstup by se měl omezit na použití jednociferných čísel (a možná 1/10 a 9/10). Pokud ano, můžete pozorovat, že existuje pouze 27 různých zlomků.

Vzhledem k tomu, že základní matematika pro generování výstupu se nikdy nezmění, řešením by mohlo být jednoduše hardwarový kód binárního vyhledávacího stromu tak, aby funkce prováděla maximálně log (27) ~ = 4 3/4 srovnání. Zde je testovaná verze C kódu

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

Zde je odkaz, který vysvětluje matematiku za převod desetinných míst na zlomek:

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

Zde je příklad funkce, jak to vlastně provést pomocí VB (z www.freevbcode.com/ShowCode.asp?ID=582):

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

(Z vyhledávání Google: převést desetinné na zlomek, převést desetinný kód na zlomek)

16
devinmoore

Možná budete chtít číst Co by měl každý počítačový vědec vědět o aritmetice s pohyblivou čárkou .

Určitou přesnost musíte zadat vynásobením velkým číslem:

3.141592 * 1000000 = 3141592

pak můžete udělat zlomek:

3 + (141592 / 1000000)

a snižovat pomocí GCD ...

3 + (17699 / 125000)

ale neexistuje žádný způsob, jak dostat zamýšlený zlomek ven. Možná budete chtít vždy použít zlomky v celém kódu místo - ale nezapomeňte snížit zlomky, když se můžete vyhnout přetečení!

9
nlucaroni

Zde jsou verze jazyka Perl a Javascript VB kód navržený devinmoore:

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

A téměř identický 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

Implementace C #

/// <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 indukuje poměrně přirozený způsob přibližování reálných čísel zlomky s jednoduchými jmenovateli.

7
Doug McClean

Část problému spočívá v tom, že tolik zlomků není ve skutečnosti snadno považováno za zlomky. Např. 0,33 není 1/3, je to 33/100. Ale pokud si vzpomenete na základní školu, pak je proces přeměny desetinných hodnot na zlomky, ale je nepravděpodobné, že by vám dal to, co chcete, protože většina časových desetinných čísel není uložena na hodnotě 0,33, ale 0,329999999999998 nebo některé takové.

Udělejte si laskavost a neobtěžujte se s tím, ale pokud potřebujete, můžete udělat následující:

Vynásobte původní hodnotu 10, dokud neodstraníte zlomkovou část. Mějte toto číslo a použijte jej jako dělitel. Pak udělejte řadu zjednodušení hledáním společných jmenovatelů.

Takže 0,4 by bylo 4/10. Ty by pak hledat společné dělitele začínající na nízké hodnoty, pravděpodobně prvočísla. Počínaje 2, uvidíte, zda 2 rozděluje oba čitatel a jmenovatel rovnoměrně kontrolou, zda podlaha divize je stejná jako divize samotná.

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

Takže 5 nerozděluje 2 rovnoměrně. Takže pak zkontrolujete další číslo, řekněme 3. Uděláte to, dokud nenarazí na nebo nad druhou odmocninu menšího čísla.

Poté, co to uděláte, pak potřebujete

5
Orion Adrian

Toto není “algoritmus”, jen Python řešení: http://docs.python.org/library/fractions.html

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

"Řekněme, že máme 0,33, musíme výstup" 1/3 "."

Jakou přesnost očekáváte "řešení"? 0,33 se nerovná 1/3. Jak poznáte „dobrou“ (snadno čitelnou) odpověď?

Nezáleží na tom, jaký možný algoritmus by mohl být:

Pokud očekáváte najít nejbližší zlomek ve tvaru X/Y, kde Y je menší než 10, pak můžete smyčku, i když všech 9 možných Ys, pro každý Y vypočítat X, a pak vyberte nejpřesnější.

4
Suma

Vestavěné řešení v R:

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

To používá metodu pokračování zlomku a má volitelné cycles a max.denominator argumenty pro úpravu přesnosti.

3
Ben Bolker

Myslím, že nejlepší způsob, jak toho dosáhnout, je nejprve převést hodnotu float na reprezentaci ascii. V C++ můžete použít ostringstream nebo C, můžete použít sprintf. Zde je návod, jak by to vypadalo v jazyce C++:

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;

Podobný přístup by mohl být učiněn na rovné C.

Poté budete muset zkontrolovat, zda je zlomek v nejnižších termínech. Tento algoritmus poskytne přesnou odpověď, tj. 0,33 bude výstup "33/100", ne "1/3". Nicméně, 0.4 by dal “4/10,” který když redukovaný k nejnižším podmínkám by byl “2/5.”\T To nemusí být tak silné jako řešení EppStein, ale věřím, že je to jednodušší. 

2
bpm

Jedním z řešení je pouze uložit všechna čísla jako racionální čísla na prvním místě. Existují knihovny pro aritmetiku racionálních čísel (např. GMP ). Pokud používáte jazyk OO, můžete použít knihovnu racionálních číselných tříd k nahrazení vaší třídy čísel.

Finanční programy by, mimo jiné, použily takové řešení, aby bylo možné provést přesné výpočty a zachovat přesnost, která může být ztracena pomocí hladkého plováku.

Samozřejmě to bude mnohem pomalejší, takže to pro vás nemusí být praktické. Záleží na tom, kolik výpočtů musíte udělat a jak důležitá je pro vás přesnost.

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

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

Odpověď v C++, za předpokladu, že máte třídu 'BigInt', která může ukládat celá čísla bez omezení.

Místo toho můžete použít 'unsigned long long', ale bude fungovat pouze pro určité hodnoty.

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) se vrátí "+0/1", takže budete chtít tento případ zpracovat odděleně.

P.S .: Tento kód používám již několik let ve své vlastní třídě „RationalNum“ a je důkladně testován.

2
barak manos

Budete muset zjistit, jakou úroveň chyb chcete přijmout. Ne všechny desetinné zlomky se sníží na jednoduchý zlomek. Pravděpodobně bych si vybral snadno dělitelné číslo, jako je 60, a zjistím, kolik 60. let je nejbližší hodnotě a pak frakci zjednoduší.

2
Mark Bessey

Tento algoritmus Ian RichardsJOHN KENNEDY NEJEN VR&AACUTE;T&IACUTE; PĚKN&EACUTE; ZLOMKY, ALE TAK&EACUTE; VELMI DOBŘE FUNGUJE Z HLEDISKA RYCHLOSTI. TOTO JE K&OACUTE;D C # PŘEVZAT&YACUTE; Z TATO ODPOVĚĎ MNOU.

TO ZVL&AACUTE;DNE V&SCARON;ECHNY double HODNOTY KROMĚ ZVL&AACUTE;&SCARON;TN&IACUTE;CH HODNOT JAKO NAN A +/- NEKONEČNO, KTER&YACUTE; BUDETE MUSET PŘIDAT V PŘ&IACUTE;PADĚ POTŘEBY.

VR&AACUTE;T&IACUTE; new Fraction(numerator, denominator). NAHRAĎTE VLASTN&IACUTE;M TYPEM.

_/V&IACUTE;CE PŘ&IACUTE;KLADN&YACUTE;CH HODNOT A POROVN&AACUTE;N&IACUTE; S JIN&YACUTE;MI ALGORITMY, GO HERE

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

Příklad hodnoty vrácené tímto algoritmem:

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

Můžete to provést v libovolném programovacím jazyce pomocí následujících kroků:

  1. Vynásobte a vydělte 10 ^ x, kde x je síla 10 potřebná k tomu, aby se zajistilo, že číslo nemá zbývající desetinná místa. Příklad: Vynásobte 0,33 o 10 ^ 2 = 100, aby bylo 33 a rozdělte jej stejným počtem, abyste získali 33/100
  2. Snižte čitatel a jmenovatel výsledné frakce faktorizací, dokud již z výsledku nemůžete získat celá čísla.
  3. Výsledná snížená frakce by měla být vaše odpověď.

Příklad: [0,4] 0,2 [.] = 0,2 x 10 1/10 ^ [.] = 2/10 [.] = 1/5

Takže to lze číst jako '1 z 5'

2
Pascal

Ruby již má zabudované řešení:

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

V aplikaci Rails lze konvertovat také číselné atributy ActiveRecord:

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

Dokončili výše uvedený kód a převedli jej na hodnotu as3

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

Zde je rychlá a špinavá implementace v javascriptu, který používá přístup brutální síly. Není vůbec optimalizován, funguje v rámci předem definovaného rozsahu zlomků: 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));

To je inspirováno přístupem JPS.

1
Deepak Joy

Budete mít dva základní problémy, které to ztěžují:

1) Plovoucí bod není přesná reprezentace, což znamená, že pokud máte zlomek „x/y“, který má za následek hodnotu „z“, může váš zlomkový algoritmus vrátit výsledek jiný než „x/y“.

2) Existuje nekonečně mnoho dalších iracionálních čísel než racionálních. Racionální číslo je číslo, které může být reprezentováno jako zlomek. Iracionální jsou ty, které nemohou.

Nicméně, levným způsobem, protože plovoucí desetinná čárka má mezní přesnost, pak ji můžete vždy reprezentovat jako nějakou formu frakce. (Myslím...)

1
Torlack

Řekněme, že máme 0,33, musíme výstup "1/3". Pokud máme "0.4", musíme Vydat "2/5".

Je to špatné v běžném případě, protože 1/3 = 0.3333333 = 0. (3) Navíc z navrhovaných řešení není možné zjistit, že desetinné číslo může být převedeno na zlomek s definovanou přesností, protože výstup je vždy zlomek.

VUT v Brně navrhuji svou komplexní funkci s mnoha možnostmi založenou na myšlence Infinite geometrické řady , konkrétně na vzorci:

enter image description here

Zpočátku se tato funkce snaží najít období zlomku v řetězcové reprezentaci. Poté se použije výše uvedený vzorec.

Kód racionálních čísel je vypůjčen z implementace racionálních čísel v jazyce { Stephen M. McKamey . Doufám, že není těžké přenést můj kód do jiných jazyků.

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

Existuje několik příkladů použití:

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;

Váš případ s ořezem pravého dílu:

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;

Demostrace min.

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.

Zaokrouhlování na konci:

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

Nejzajímavější případ:

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.

Ostatní testy a kódy si každý najde v moje knihovna MathFunctions na github .

1
Ivan Kochurkin

Narazil jsem na obzvláště elegantní řešení Haskell, které využívá anamorfózy. Záleží na balíčku rekurze-schémata .

{-# 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)

Pokud to zkusíte v ghci, opravdu to funguje!

λ:> approximate pi 2
22 % 7
0
user8174234

Jak mnoho lidí uvedlo, že opravdu nemůžete převést plovoucí desetinnou čárku zpět na zlomek (ledaže je to extrémně přesné .25). Samozřejmě můžete vytvořit nějaký typ vyhledávání pro velké množství zlomků a použít nějaký druh fuzzy logiky k vytvoření výsledku, který hledáte. Opět by to nebylo přesné a vy byste museli definovat dolní hranice toho, jak velká je vaše touha po jmenovateli.

.32 <x <.34 = 1/3 nebo něco podobného.

0
Tim

Zde je implementace pro Ruby 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