当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何将浮点数转换为人类可读的分数?》经验,为你挑选了10个好方法。

假设我们有0.33,我们需要输出"1/3".
如果我们有"0.4",我们需要输出"2/5".

我们的想法是让人们可读,让用户理解"y部分中的x部分",作为理解数据的更好方式.

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



1> 小智..:

我发现David Eppstein 发现给定实数 C代码的理性近似正是你所要求的.它基于连续分数理论,非常快速且相当紧凑.

我已经为特定的分子和分母限制使用了这个版本.

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

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


请注意,有一些边缘情况,此代码不能很好地处理:当给定-1.3333333,最大分母为4时,它返回4/-3,错误为3.333333e-08和-5/4,错误= -8.333330e-02,这是正确的.但是当给定-1.33333337具有相同的最大分母时,它变为12121211/-9090908,误差为误差= 4.218847e-15和-4/3,误差为-3.666667e-08,这是不正确的.特别是当使用计算的浮点数(例如-4/3)呈现算法时,这是一个问题,这会产生不正确的结果.
对于那些在Ruby中寻找解决方案的人,我们很幸运!Christopher Lord在Ruby gem中实现了上述算法.见http://christopher.lord.ac/fractions-in-ruby和http://rubygems.org/gems/fraction

2> Debilski..:

从Python 2.6开始就有fractions模块.

(引用文档.)

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


http://hg.python.org/cpython/file/822c7c0d27d1/Lib/fractions.py#l211上的实现和算法说明
@vladr好吧,鉴于我差不多6年前写过这个答案(问题问题已经过了一年多),我想我不知道我的推理是什么了.最有可能的是我指的是这个评论:http://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions/1992522?noredirect = 1#comment14980_95727 OTOH它也可能是那个这个答案已经从另一个问题中合并了.这些年后谁能分辨出来......

3> J P..:

如果输出是为了给人类读者一个结果顺序的快速印象,那么返回"113/211"之类的东西是没有意义的,所以输出应该限制为使用一位数字(也许是1/10和9/10).如果是这样,您可以观察到只有27 种不同的分数.

由于用于生成输出的基础数学将永远不会改变,因此解决方案可能是简单地对二进制搜索树进行硬编码,以便该函数最多执行log(27)〜= 4 3/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> devinmoore..:

这是一个解释将小数转换为分数的数学的链接:

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

这是一个如何使用VB实际执行此操作的示例函数(来自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

(来自谷歌搜索:将小数转换为分数,将小数转换为分数代码)



5> nlucaroni..:

您可能想要阅读每个计算机科学家应该知道的关于浮点运算的内容.

您必须通过乘以一个大数字来指定一些精度:

3.141592 * 1000000 = 3141592

然后你可以做一个分数:

3 + (141592 / 1000000)

并通过GCD减少......

3 + (17699 / 125000)

但没有办法得到预期的分数.您可能希望始终在整个代码中使用分数 - 只要记住在可以避免溢出时减少分数!



6> mivk..:

这是devinmoore建议的VB代码的Perl和Javascript版本:

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

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



7> Tom..:

AC#实施

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

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

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

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

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


    /// 
    /// Approximates the provided value to a fraction.
    /// http://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions
    /// 
    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);
    }
}



8> Doug McClean..:

在斯特恩Brocot树引起了相当自然的方式通过简单的分母分数近似实数.



9> Orion Adrian..:

部分问题在于,如此多的分数实际上不容易被解释为分数.例如0.33不是1/3,它是33/100.但是如果你还记得你的小学培训,那么有一个将十进制值转换成分数的过程,但是它不太可能给你你想要的东西,因为大多数时候十进制数不是存储在0.33,而是0.329999999999998或其他一些.

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

将原始值乘以10,直到删除小数部分.保留该数字,并将其用作除数.然后通过寻找共同点进行一系列简化.

所以0.4将是4/10.然后,您将寻找以低值开头的公约数,可能是素数.从2开始,你会看到2是否通过检查除法的底限是否与除法本身相同来均匀地除以分子和分母.

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

所以5不均匀分为2.那么你检查下一个数字,比如3.你这样做直到你达到或低于较小数字的平方根.

在你这样做之后,你需要



10> 小智..:

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

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

推荐阅读
mylvfamily
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有