如何将浮点数转换为人类可读的分数?

时间:2022-11-24 20:53:19

Let's say we have 0.33, we need to output "1/3".
If we have "0.4", we need to output "2/5".

假设我们有0。33,我们需要输出“1/3”如果我们有“0.4”,我们需要输出“2/5”。

The idea is to make it human-readable to make the user understand "x parts out of y" as a better way of understanding data.

这个想法是为了让用户能够理解“y”中的“x部分”,从而更好地理解数据。

I know that percentages is a good substitute but I was wondering if there was a simple way to do this?

我知道百分比是一个很好的替代品,但我想知道是否有一个简单的方法可以做到这一点?

26 个解决方案

#1


64  

I have found David Eppstein's find rational approximation to given real number C code to be exactly what you are asking for. Its based on the theory of continued fractions and very fast and fairly compact.

我发现David Eppstein找到了合理的近似,给出了实数C代码,这正是你所要求的。它基于连续分数理论,而且非常快,非常紧凑。

I have used versions of this customized for specific numerator and denominator limits.

我已经使用了这个定制的版本,用于特定的分子和分母的限制。

/*
** 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]));
}

#2


24  

From Python 2.6 on there is the fractions module.

在Python 2.6中有一个分数模块。

(Quoting from the docs.)

(引用文档。)

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

#3


21  

If the the output is to give a human reader a fast impression of the order of the result, it makes no sense return something like "113/211", so the output should limit itself to using one-digit numbers (and maybe 1/10 and 9/10). If so, you can observe that there are only 27 different fractions.

如果输出是为了让一个人对结果的顺序有一个快速的印象,它就没有任何意义返回像“113/211”,所以输出应该限制自己使用一位数的数字(也可能是1/10和9/10)。如果是这样,你可以观察到只有27个不同的分数。

Since the underlying math for generating the output will never change, a solution could be to simply hard-code a binary search tree, so that the function would perform at most log(27) ~= 4 3/4 comparisons. Here is a tested C version of the code

因为生成输出的基础数学永远不会改变,所以一个解决方案可能就是硬编码一个二叉搜索树,使函数在大多数日志(27)~= 4 /4的比较中执行。这是一个经过测试的C版本的代码。

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

#4


16  

Here's a link explaining the math behind converting a decimal to a fraction:

这里有一个链接,解释将小数化为分数的数学原理:

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

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

And here's an example function for how to actually do it using VB (from www.freevbcode.com/ShowCode.asp?ID=582):

下面是一个如何使用VB实现它的示例函数(来自www.freevbcode.com/showcode.asp?

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

(From google searches: convert decimal to fraction, convert decimal to fraction code)

(从谷歌搜索:将小数化为分数,将小数化为分数代码)

#5


9  

You might want to read What Every Computer Scientist Should Know about Floating Point Arithmetic.

你可能想要阅读每个计算机科学家都应该知道的关于浮点运算的知识。

You'll have to specify some precision by multiplying by a large number:

你需要通过乘以一个大的数来指定一些精度:

3.141592 * 1000000 = 3141592

then you can make a fraction:

然后你可以做一个分数:

3 + (141592 / 1000000)

and reduce via GCD...

通过肾小球囊性肾病和减少……

3 + (17699 / 125000)

but there is no way to get the intended fraction out. You might want to always use fractions throughout your code instead --just remember to reduce fractions when you can to avoid overflow!

但是没有办法把预期的分数拿出来。您可能想要始终在代码中使用分数——只要记住在可以避免溢出的时候减少分数!

#6


9  

A C# implementation

一个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://*.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);
    }
}

#7


7  

The Stern-Brocot Tree induces a fairly natural way to approximate real numbers by fractions with simple denominators.

这棵树的树诱导了一种相当自然的方法,用简单的分母来近似实数。

#8


7  

Here are Perl and Javascript versions of the VB code suggested by devinmoore:

以下是devinmoore提出的VB代码的Perl和Javascript版本:

Perl:

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

And the almost identical javascript:

几乎相同的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


5  

Part of the problem is that so many fractions aren't actually easily construed as fractions. E.g. 0.33 isn't 1/3, it's 33/100. But if you remember your elementary school training, then there is a process of converting decimal values into fractions, however it's unlikely to give you what you want since most of the time decimal numbers aren't stored at 0.33, but 0.329999999999998 or some such.

部分问题在于,如此多的分数实际上并不容易被理解成分数。0。33不是1/3,是33/100。但是如果你还记得你的小学训练,那么就有一个把十进制值转换成分数的过程,但是它不太可能给你你想要的东西,因为大多数时候十进制数不存储在0.33,但是0。329999999999998或一些这样的。

Do yourself a favor and don't bother with this, but if you need to then you can do the following:

帮自己一个忙,不要为此烦恼,但如果你需要,你可以做以下事情:

Multiply the original value by 10 until you remove the fractional part. Keep that number, and use it as the divisor. Then do a series of simplifications by looking for common denominators.

将原来的值乘以10,直到去掉小数部分。保留这个数,用它作为除数。然后进行一系列的简化,寻找共同的分母。

So 0.4 would be 4/10. You would then look for common divisors starting with low values, probably prime numbers. Starting with 2, you would see if 2 divides both the numerator and denominator evenly by checking if the floor of division is the same as the division itself.

所以0。4等于4/10。然后,您将从低值开始查找常见的除数,可能是素数。从2开始,你会看到,如果两个分子和分母同时除以2,那么除法的下限是否等于除法本身。

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

So 5 does not divide 2 evenly. So then you check the next number, say 3. You do this until you hit at or above the square root of the smaller number.

所以5不能平均除以2。然后你检查下一个数字,比如3。你这样做,直到你达到或超过这个小数字的平方根。

After you do that then you need

在你这么做之后,你需要。

#10


5  

This is not an "algorithm", just a Python solution: http://docs.python.org/library/fractions.html

这不是一个“算法”,只是一个Python解决方案:http://docs.python.org/library/fractions.html。

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

#11


4  

"Let's say we have 0.33, we need to output "1/3". "

假设我们有0。33,我们需要输出1/3。”

What precision do you expect the "solution" to have? 0.33 is not equal to 1/3. How do you recognize a "good" (easy to read) answer?

你希望“解决方案”有什么精度?0。33不等于1/3。你如何识别一个“好”(容易阅读)的答案?

No matter what, a possible algorithm could be:

无论如何,一种可能的算法是:

If you expect to find a nearest fraction in a form X/Y where Y is less then 10, then you can loop though all 9 possible Ys, for each Y compute X, and then select the most accurate one.

如果你期望在X/Y中找到一个最接近的分数,Y小于10,那么你可以循环,尽管所有9个可能的Ys,对于每一个Y计算X,然后选择最精确的。

#12


3  

A built-in solution in R:

R中内置的解决方案:

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

This uses a continued fraction method and has optional cycles and max.denominator arguments for adjusting the precision.

这使用了一个连续的分数方法,并且有可选的周期和最大值。

#13


2  

You'll have to figure out what level of error you're willing to accept. Not all decimal fractions will reduce to a simple fraction. I'd probably pick an easily-divisible number, like 60, and figure out how many 60ths is closest to the value, then simplify the fraction.

你必须弄清楚你愿意接受什么样的错误。不是所有的小数部分都能简化成一个简单的分数。我可能会选择一个易于分割的数字,比如60,然后算出有多少60ths最接近这个值,然后简化分数。

#14


2  

You can do this in any programming language using the following steps:

您可以在任何编程语言中使用以下步骤:

  1. Multiply and Divide by 10^x where x is the power of 10 required to make sure that the number has no decimal places remaining. Example: Multiply 0.33 by 10^2 = 100 to make it 33 and divide it by the same to get 33/100
  2. 乘法和除法10 ^ x,x等于10的力量需要确保没有小数点后剩余的数量。例子:0.33乘以10 ^ 2 = 100,33,除以相同的33/100
  3. Reduce the numerator and the denominator of the resulting fraction by factorization, till you can no longer obtain integers from the result.
  4. 将结果分数的分子和分母分解,直到你不能从结果中得到整数。
  5. The resulting reduced fraction should be your answer.
  6. 得到的减少分数应该是你的答案。

Example: 0.2 =0.2 x 10^1/10^1 =2/10 =1/5

例如:0.2 = 0.2 x 10 ^ ^ 1 = 2/10 = 2/10 1/10

So, that can be read as '1 part out of 5'

所以,这可以被解读为" 1 / 5 "

#15


2  

One solution is to just store all numbers as rational numbers in the first place. There are libraries for rational number arithmetic (eg GMP). If using an OO language you may be able to just use a rational number class library to replace your number class.

一种解决方案是把所有的数字都作为合理的数字存储在一开始。有一些用于合理数字运算的库(如GMP)。如果使用OO语言,您可以使用rational number类库来替换您的number类。

Finance programs, among others, would use such a solution to be able to make exact calculations and preserve precision that may be lost using a plain float.

在其他一些项目中,金融项目将使用这样的解决方案来精确计算并保持精确的精度,而这可能是用普通的浮点数来计算的。

Of course it will be a lot slower so it may not be practical for you. Depends on how much calculations you need to do, and how important the precision is for you.

当然,它会慢得多,所以对你来说可能不实用。这取决于你需要做多少计算,以及精确度对你来说有多重要。

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

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

#16


2  

I think the best way to do this is to first convert your float value to an ascii representation. In C++ you could use ostringstream or in C, you could use sprintf. Here's how it would look in C++:

我认为最好的方法是先将浮点值转换为ascii表示。在c++中,可以使用ostringstream或C,可以使用sprintf。下面是它在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;

A similar approach could be taken in straight C.

同样的方法也可以用C来表示。

Afterwards you would need to check that the fraction is in lowest terms. This algorithm will give a precise answer, i.e. 0.33 would output "33/100", not "1/3." However, 0.4 would give "4/10," which when reduced to lowest terms would be "2/5." This may not be as powerful as EppStein's solution, but I believe this is more straightforward.

然后你需要检查这个分数是最低的。这个算法给出一个精确的答案,即0.33将输出“33/100”,而不是“1/3”。但是,0。4会给出“4/10”,当降到最低时,会是“2/5”。这可能不如EppStein的解决方案那么强大,但我相信这更直接。

#17


2  

Ruby already has a built in solution:

Ruby已经有一个内置的解决方案:

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

In Rails, ActiveRecord numerical attributes can be converted too:

在Rails中,ActiveRecord的数字属性也可以被转换:

product.size = 0.33
product.size.to_r.to_s # => "33/100"

#18


2  

Answer in C++, assuming that you have a 'BigInt' class, which can store unlimited-size integers.

在c++中,假设您有一个“BigInt”类,它可以存储无限大小的整数。

You can use 'unsigned long long' instead, but it will only work for certain values.

你可以用“unsigned long long”代替,但它只适用于特定的值。

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) will return "+0/1", so you might wanna handle this case separately.

BTW, GetRational(0.0)将返回“+0/1”,因此您可能想单独处理这个案例。

P.S.: I've been using this code in my own 'RationalNum' class for several years, and it's been tested thoroughly.

注:我在自己的“RationalNum”课程中使用了好几年的代码,并且已经被彻底测试过了。

#19


1  

You are going to have two basic problems that will make this hard:

你将会有两个基本问题,这将使这很难:

1) Floating point isn't an exact representation which means that if you have a fraction of "x/y" which results in a value of "z", your fraction algorithm may return a result other than "x/y".

1)浮点数不是一个精确的表示,这意味着如果你有一个“x/y”的分数,结果是“z”的值,那么你的分数算法可能会返回一个结果,而不是“x/y”。

2) There are infinity many more irrational numbers than rational. A rational number is one that can be represented as a fraction. Irrational being ones that can not.

有无穷多的无理数比理性的多。一个合理的数字可以表示为分数。不理性的人不能。

However, in a cheap sort of way, since floating point has limit accuracy, then you can always represent it as some form of faction. (I think...)

然而,以一种廉价的方式,因为浮点有限制的准确性,那么你就可以将它作为某种形式的阵营来表示。(我认为…)

#20


1  

Completed the above code and converted it to as3

完成上述代码并将其转换为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";
    }

#21


1  

Let's say we have 0.33, we need to output "1/3". If we have "0.4", we need to output "2/5".

假设我们有0。33,我们需要输出“1/3”如果我们有“0.4”,我们需要输出“2/5”。

It's wrong in common case, because of 1/3 = 0.3333333 = 0.(3) Moreover, it's impossible to find out from suggested above solutions is decimal can be converted to fraction with defined precision, because output is always fraction.

在一般情况下,由于1/3 = 0.3333333 = 0,这是错误的,而且,不可能从上面的建议中看出,小数可以被转换成具有定义精度的分数,因为输出总是分数。

BUT, i suggest my comprehensive function with many options based on idea of Infinite geometric series, specifically on formula:

但是,我提出了基于无限几何级数的多种选择的综合函数,具体的公式如下:

如何将浮点数转换为人类可读的分数?

At first this function is trying to find period of fraction in string representation. After that described above formula is applied.

首先,这个函数尝试在字符串表示中找到分数的周期。然后应用上述公式。

Rational numbers code is borrowed from Stephen M. McKamey rational numbers implementation in C#. I hope there is not very hard to port my code on other languages.

Rational numbers代码是从Stephen M. McKamey的Rational数字实现中借用的c#。我希望我的代码在其他语言上不太困难。

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

There are some examples of usings:

这里有一些例子:

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;

Your case with right part zero part trimming:

你的箱子的右边部分是零配件:

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;

Min period demostration:

分钟时间骨干示范:

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.

Rounding at the end:

四舍五入结束时:

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

The most interesting case:

最有趣的例子:

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.

Other tests and code everyone can find in my MathFunctions library on github.

其他测试和代码,每个人都可以在github上的mathfunction库中找到。

#22


1  

Here is a quick and dirty implementation in javascript that uses a brute force approach. Not at all optimized, it works within a predefined range of fractions: http://jsfiddle.net/PdL23/1/

这里有一个javascript的快速和脏的实现,使用了蛮力方法。完全没有优化,它在一个预定义的分数范围内工作: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));

This is inspired by the approach used by JPS.

这是由JPS使用的方法所激发的。

#23


1  

This algorithm by Ian Richards / John Kennedy not only returns nice fractions, it also performs very well in terms of speed. This is C# code as taken from this answer by me.

这个算法由Ian Richards / John Kennedy不仅返回了漂亮的分数,而且在速度方面也表现得很好。这是我从这个答案中得到的c#代码。

It can handle all double values except special values like NaN and +/- infinity, which you'll have to add if needed.

它可以处理除特殊值(如NaN和+/-∞)之外的所有双值,如果需要,您还需要添加这些值。

It returns a new Fraction(numerator, denominator). Replace by your own type.

它返回一个新的分数(分子,分母)。以您自己的类型替换。

For more example values and a comparison with other algorithms, 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);
}

Example values returned by this algorithm:

该算法返回的示例值:

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         

#24


0  

As many people have stated you really can't convert a floating point back to a fraction (unless its extremely exact like .25). Of course you could create some type of look up for a large array of fractions and use some sort of fuzzy logic to produce the result you are looking for. Again this wouldn't be exact though and you would need to define a lower bounds of how large your want the denominator to go.

正如许多人所说,你真的不能把浮点数转换成分数(除非它非常精确地像。25)。当然,您可以创建一些类型的查找,以获取大量的分数,并使用某种模糊逻辑来生成您正在寻找的结果。同样的,这也不是精确的,你需要定义一个下界你的要求是多大的分母。

.32 < x < .34 = 1/3 or something like that.

.32 < x <。34 = 1/3或类似的东西。

#25


0  

Here is implementation for ruby http://github.com/valodzka/frac

下面是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)

#26


0  

I came across an especially elegant Haskell solution making use of an anamorphism. It depends on the recursion-schemes package.

我遇到了一个特别优雅的Haskell解决方案,它利用了一个anamorphism。这取决于递归方案包。

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

If you try this out in ghci, it really does work!

如果你在ghci中尝试这个,它真的有用!

λ:> approximate pi 2
22 % 7

#1


64  

I have found David Eppstein's find rational approximation to given real number C code to be exactly what you are asking for. Its based on the theory of continued fractions and very fast and fairly compact.

我发现David Eppstein找到了合理的近似,给出了实数C代码,这正是你所要求的。它基于连续分数理论,而且非常快,非常紧凑。

I have used versions of this customized for specific numerator and denominator limits.

我已经使用了这个定制的版本,用于特定的分子和分母的限制。

/*
** 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]));
}

#2


24  

From Python 2.6 on there is the fractions module.

在Python 2.6中有一个分数模块。

(Quoting from the docs.)

(引用文档。)

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

#3


21  

If the the output is to give a human reader a fast impression of the order of the result, it makes no sense return something like "113/211", so the output should limit itself to using one-digit numbers (and maybe 1/10 and 9/10). If so, you can observe that there are only 27 different fractions.

如果输出是为了让一个人对结果的顺序有一个快速的印象,它就没有任何意义返回像“113/211”,所以输出应该限制自己使用一位数的数字(也可能是1/10和9/10)。如果是这样,你可以观察到只有27个不同的分数。

Since the underlying math for generating the output will never change, a solution could be to simply hard-code a binary search tree, so that the function would perform at most log(27) ~= 4 3/4 comparisons. Here is a tested C version of the code

因为生成输出的基础数学永远不会改变,所以一个解决方案可能就是硬编码一个二叉搜索树,使函数在大多数日志(27)~= 4 /4的比较中执行。这是一个经过测试的C版本的代码。

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

#4


16  

Here's a link explaining the math behind converting a decimal to a fraction:

这里有一个链接,解释将小数化为分数的数学原理:

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

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

And here's an example function for how to actually do it using VB (from www.freevbcode.com/ShowCode.asp?ID=582):

下面是一个如何使用VB实现它的示例函数(来自www.freevbcode.com/showcode.asp?

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

(From google searches: convert decimal to fraction, convert decimal to fraction code)

(从谷歌搜索:将小数化为分数,将小数化为分数代码)

#5


9  

You might want to read What Every Computer Scientist Should Know about Floating Point Arithmetic.

你可能想要阅读每个计算机科学家都应该知道的关于浮点运算的知识。

You'll have to specify some precision by multiplying by a large number:

你需要通过乘以一个大的数来指定一些精度:

3.141592 * 1000000 = 3141592

then you can make a fraction:

然后你可以做一个分数:

3 + (141592 / 1000000)

and reduce via GCD...

通过肾小球囊性肾病和减少……

3 + (17699 / 125000)

but there is no way to get the intended fraction out. You might want to always use fractions throughout your code instead --just remember to reduce fractions when you can to avoid overflow!

但是没有办法把预期的分数拿出来。您可能想要始终在代码中使用分数——只要记住在可以避免溢出的时候减少分数!

#6


9  

A C# implementation

一个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://*.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);
    }
}

#7


7  

The Stern-Brocot Tree induces a fairly natural way to approximate real numbers by fractions with simple denominators.

这棵树的树诱导了一种相当自然的方法,用简单的分母来近似实数。

#8


7  

Here are Perl and Javascript versions of the VB code suggested by devinmoore:

以下是devinmoore提出的VB代码的Perl和Javascript版本:

Perl:

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

And the almost identical javascript:

几乎相同的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


5  

Part of the problem is that so many fractions aren't actually easily construed as fractions. E.g. 0.33 isn't 1/3, it's 33/100. But if you remember your elementary school training, then there is a process of converting decimal values into fractions, however it's unlikely to give you what you want since most of the time decimal numbers aren't stored at 0.33, but 0.329999999999998 or some such.

部分问题在于,如此多的分数实际上并不容易被理解成分数。0。33不是1/3,是33/100。但是如果你还记得你的小学训练,那么就有一个把十进制值转换成分数的过程,但是它不太可能给你你想要的东西,因为大多数时候十进制数不存储在0.33,但是0。329999999999998或一些这样的。

Do yourself a favor and don't bother with this, but if you need to then you can do the following:

帮自己一个忙,不要为此烦恼,但如果你需要,你可以做以下事情:

Multiply the original value by 10 until you remove the fractional part. Keep that number, and use it as the divisor. Then do a series of simplifications by looking for common denominators.

将原来的值乘以10,直到去掉小数部分。保留这个数,用它作为除数。然后进行一系列的简化,寻找共同的分母。

So 0.4 would be 4/10. You would then look for common divisors starting with low values, probably prime numbers. Starting with 2, you would see if 2 divides both the numerator and denominator evenly by checking if the floor of division is the same as the division itself.

所以0。4等于4/10。然后,您将从低值开始查找常见的除数,可能是素数。从2开始,你会看到,如果两个分子和分母同时除以2,那么除法的下限是否等于除法本身。

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

So 5 does not divide 2 evenly. So then you check the next number, say 3. You do this until you hit at or above the square root of the smaller number.

所以5不能平均除以2。然后你检查下一个数字,比如3。你这样做,直到你达到或超过这个小数字的平方根。

After you do that then you need

在你这么做之后,你需要。

#10


5  

This is not an "algorithm", just a Python solution: http://docs.python.org/library/fractions.html

这不是一个“算法”,只是一个Python解决方案:http://docs.python.org/library/fractions.html。

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

#11


4  

"Let's say we have 0.33, we need to output "1/3". "

假设我们有0。33,我们需要输出1/3。”

What precision do you expect the "solution" to have? 0.33 is not equal to 1/3. How do you recognize a "good" (easy to read) answer?

你希望“解决方案”有什么精度?0。33不等于1/3。你如何识别一个“好”(容易阅读)的答案?

No matter what, a possible algorithm could be:

无论如何,一种可能的算法是:

If you expect to find a nearest fraction in a form X/Y where Y is less then 10, then you can loop though all 9 possible Ys, for each Y compute X, and then select the most accurate one.

如果你期望在X/Y中找到一个最接近的分数,Y小于10,那么你可以循环,尽管所有9个可能的Ys,对于每一个Y计算X,然后选择最精确的。

#12


3  

A built-in solution in R:

R中内置的解决方案:

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

This uses a continued fraction method and has optional cycles and max.denominator arguments for adjusting the precision.

这使用了一个连续的分数方法,并且有可选的周期和最大值。

#13


2  

You'll have to figure out what level of error you're willing to accept. Not all decimal fractions will reduce to a simple fraction. I'd probably pick an easily-divisible number, like 60, and figure out how many 60ths is closest to the value, then simplify the fraction.

你必须弄清楚你愿意接受什么样的错误。不是所有的小数部分都能简化成一个简单的分数。我可能会选择一个易于分割的数字,比如60,然后算出有多少60ths最接近这个值,然后简化分数。

#14


2  

You can do this in any programming language using the following steps:

您可以在任何编程语言中使用以下步骤:

  1. Multiply and Divide by 10^x where x is the power of 10 required to make sure that the number has no decimal places remaining. Example: Multiply 0.33 by 10^2 = 100 to make it 33 and divide it by the same to get 33/100
  2. 乘法和除法10 ^ x,x等于10的力量需要确保没有小数点后剩余的数量。例子:0.33乘以10 ^ 2 = 100,33,除以相同的33/100
  3. Reduce the numerator and the denominator of the resulting fraction by factorization, till you can no longer obtain integers from the result.
  4. 将结果分数的分子和分母分解,直到你不能从结果中得到整数。
  5. The resulting reduced fraction should be your answer.
  6. 得到的减少分数应该是你的答案。

Example: 0.2 =0.2 x 10^1/10^1 =2/10 =1/5

例如:0.2 = 0.2 x 10 ^ ^ 1 = 2/10 = 2/10 1/10

So, that can be read as '1 part out of 5'

所以,这可以被解读为" 1 / 5 "

#15


2  

One solution is to just store all numbers as rational numbers in the first place. There are libraries for rational number arithmetic (eg GMP). If using an OO language you may be able to just use a rational number class library to replace your number class.

一种解决方案是把所有的数字都作为合理的数字存储在一开始。有一些用于合理数字运算的库(如GMP)。如果使用OO语言,您可以使用rational number类库来替换您的number类。

Finance programs, among others, would use such a solution to be able to make exact calculations and preserve precision that may be lost using a plain float.

在其他一些项目中,金融项目将使用这样的解决方案来精确计算并保持精确的精度,而这可能是用普通的浮点数来计算的。

Of course it will be a lot slower so it may not be practical for you. Depends on how much calculations you need to do, and how important the precision is for you.

当然,它会慢得多,所以对你来说可能不实用。这取决于你需要做多少计算,以及精确度对你来说有多重要。

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

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

#16


2  

I think the best way to do this is to first convert your float value to an ascii representation. In C++ you could use ostringstream or in C, you could use sprintf. Here's how it would look in C++:

我认为最好的方法是先将浮点值转换为ascii表示。在c++中,可以使用ostringstream或C,可以使用sprintf。下面是它在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;

A similar approach could be taken in straight C.

同样的方法也可以用C来表示。

Afterwards you would need to check that the fraction is in lowest terms. This algorithm will give a precise answer, i.e. 0.33 would output "33/100", not "1/3." However, 0.4 would give "4/10," which when reduced to lowest terms would be "2/5." This may not be as powerful as EppStein's solution, but I believe this is more straightforward.

然后你需要检查这个分数是最低的。这个算法给出一个精确的答案,即0.33将输出“33/100”,而不是“1/3”。但是,0。4会给出“4/10”,当降到最低时,会是“2/5”。这可能不如EppStein的解决方案那么强大,但我相信这更直接。

#17


2  

Ruby already has a built in solution:

Ruby已经有一个内置的解决方案:

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

In Rails, ActiveRecord numerical attributes can be converted too:

在Rails中,ActiveRecord的数字属性也可以被转换:

product.size = 0.33
product.size.to_r.to_s # => "33/100"

#18


2  

Answer in C++, assuming that you have a 'BigInt' class, which can store unlimited-size integers.

在c++中,假设您有一个“BigInt”类,它可以存储无限大小的整数。

You can use 'unsigned long long' instead, but it will only work for certain values.

你可以用“unsigned long long”代替,但它只适用于特定的值。

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) will return "+0/1", so you might wanna handle this case separately.

BTW, GetRational(0.0)将返回“+0/1”,因此您可能想单独处理这个案例。

P.S.: I've been using this code in my own 'RationalNum' class for several years, and it's been tested thoroughly.

注:我在自己的“RationalNum”课程中使用了好几年的代码,并且已经被彻底测试过了。

#19


1  

You are going to have two basic problems that will make this hard:

你将会有两个基本问题,这将使这很难:

1) Floating point isn't an exact representation which means that if you have a fraction of "x/y" which results in a value of "z", your fraction algorithm may return a result other than "x/y".

1)浮点数不是一个精确的表示,这意味着如果你有一个“x/y”的分数,结果是“z”的值,那么你的分数算法可能会返回一个结果,而不是“x/y”。

2) There are infinity many more irrational numbers than rational. A rational number is one that can be represented as a fraction. Irrational being ones that can not.

有无穷多的无理数比理性的多。一个合理的数字可以表示为分数。不理性的人不能。

However, in a cheap sort of way, since floating point has limit accuracy, then you can always represent it as some form of faction. (I think...)

然而,以一种廉价的方式,因为浮点有限制的准确性,那么你就可以将它作为某种形式的阵营来表示。(我认为…)

#20


1  

Completed the above code and converted it to as3

完成上述代码并将其转换为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";
    }

#21


1  

Let's say we have 0.33, we need to output "1/3". If we have "0.4", we need to output "2/5".

假设我们有0。33,我们需要输出“1/3”如果我们有“0.4”,我们需要输出“2/5”。

It's wrong in common case, because of 1/3 = 0.3333333 = 0.(3) Moreover, it's impossible to find out from suggested above solutions is decimal can be converted to fraction with defined precision, because output is always fraction.

在一般情况下,由于1/3 = 0.3333333 = 0,这是错误的,而且,不可能从上面的建议中看出,小数可以被转换成具有定义精度的分数,因为输出总是分数。

BUT, i suggest my comprehensive function with many options based on idea of Infinite geometric series, specifically on formula:

但是,我提出了基于无限几何级数的多种选择的综合函数,具体的公式如下:

如何将浮点数转换为人类可读的分数?

At first this function is trying to find period of fraction in string representation. After that described above formula is applied.

首先,这个函数尝试在字符串表示中找到分数的周期。然后应用上述公式。

Rational numbers code is borrowed from Stephen M. McKamey rational numbers implementation in C#. I hope there is not very hard to port my code on other languages.

Rational numbers代码是从Stephen M. McKamey的Rational数字实现中借用的c#。我希望我的代码在其他语言上不太困难。

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

There are some examples of usings:

这里有一些例子:

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;

Your case with right part zero part trimming:

你的箱子的右边部分是零配件:

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;

Min period demostration:

分钟时间骨干示范:

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.

Rounding at the end:

四舍五入结束时:

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

The most interesting case:

最有趣的例子:

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.

Other tests and code everyone can find in my MathFunctions library on github.

其他测试和代码,每个人都可以在github上的mathfunction库中找到。

#22


1  

Here is a quick and dirty implementation in javascript that uses a brute force approach. Not at all optimized, it works within a predefined range of fractions: http://jsfiddle.net/PdL23/1/

这里有一个javascript的快速和脏的实现,使用了蛮力方法。完全没有优化,它在一个预定义的分数范围内工作: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));

This is inspired by the approach used by JPS.

这是由JPS使用的方法所激发的。

#23


1  

This algorithm by Ian Richards / John Kennedy not only returns nice fractions, it also performs very well in terms of speed. This is C# code as taken from this answer by me.

这个算法由Ian Richards / John Kennedy不仅返回了漂亮的分数,而且在速度方面也表现得很好。这是我从这个答案中得到的c#代码。

It can handle all double values except special values like NaN and +/- infinity, which you'll have to add if needed.

它可以处理除特殊值(如NaN和+/-∞)之外的所有双值,如果需要,您还需要添加这些值。

It returns a new Fraction(numerator, denominator). Replace by your own type.

它返回一个新的分数(分子,分母)。以您自己的类型替换。

For more example values and a comparison with other algorithms, 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);
}

Example values returned by this algorithm:

该算法返回的示例值:

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         

#24


0  

As many people have stated you really can't convert a floating point back to a fraction (unless its extremely exact like .25). Of course you could create some type of look up for a large array of fractions and use some sort of fuzzy logic to produce the result you are looking for. Again this wouldn't be exact though and you would need to define a lower bounds of how large your want the denominator to go.

正如许多人所说,你真的不能把浮点数转换成分数(除非它非常精确地像。25)。当然,您可以创建一些类型的查找,以获取大量的分数,并使用某种模糊逻辑来生成您正在寻找的结果。同样的,这也不是精确的,你需要定义一个下界你的要求是多大的分母。

.32 < x < .34 = 1/3 or something like that.

.32 < x <。34 = 1/3或类似的东西。

#25


0  

Here is implementation for ruby http://github.com/valodzka/frac

下面是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)

#26


0  

I came across an especially elegant Haskell solution making use of an anamorphism. It depends on the recursion-schemes package.

我遇到了一个特别优雅的Haskell解决方案,它利用了一个anamorphism。这取决于递归方案包。

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

If you try this out in ghci, it really does work!

如果你在ghci中尝试这个,它真的有用!

λ:> approximate pi 2
22 % 7