如何输出分数而不是小数?

7
在C++中,当我计算2/3时,它会输出小数值,如何才能只获取原格式(即2/3),而不是0.66666667?
谢谢

1
这取决于你所说的“计算2/3”是什么意思。 - Itamar Katz
3
此问题应附有代码示例,以及实际输出和期望输出。 - Björn Pollex
这里有一个有趣的演示,展示了将十进制转换为分数的工作原理:http://www.webmath.com/dec2fract.html - Tony
@Tony:是的,这很有趣,但远非数学严谨,因此我觉得它对于真正理解这个主题是非常误导的。不过,这确实是一个开始:-)。如果想要更彻底的解释,请参见例如http://en.wikipedia.org/wiki/Continued_fractions,特别是“区间内的最佳有理数”一节。(抱歉,我是一个数学家,忍不住要说...)。 - sleske
让我想起了“355/113”。 - user3528438
13个回答

10

+1:显而易见的解决方案 - 不要丢弃那些信息,确实! - Eamon Nerbonne
@Inverse:没有任何合理的方法可以做到这一点。你似乎在建议通过将浮点值乘以所有可能的分母,直到找到一个接近整数值的结果的方法?显然,这具有基本无限的算法复杂度(是否有更好的方法?)。即使这样也无法像OP所要求的那样恢复“原始格式”;它无法区分2/3和4/6。 - Oliver Charlesworth
@sleske:即使每对32位整数都产生了该值,它们的数量仍然是有限的,小于2^64。 - Inverse
@Inverse:你如何区分66666667/100000000和2/3? - Oliver Charlesworth
@Inverse:关于“以66666667/100000000为起点[...]然后运行某种GCD变体”的问题。是的,如果FP可以准确地表示所有分数,那么这将起作用。但是,它不能,因此在大多数情况下,从FP表示中得到的分数与您开始使用的分数并不完全相等。然后,您需要一些近似算法(例如我在答案中解释的那样 :-))。 - sleske
显示剩余5条评论

8

如果我理解正确,您有一个浮点数 (一个 floatdouble 类型的变量),并且您想将此值输出为分数。

如果是这种情况,您需要进一步说明您的问题:

  • FP 数字本质上就是一个分数:FP 数字由两个整数组成,一个尾数 m 和一个指数 e (以及一个符号,但这在这里无关紧要)。因此,每个 FP 数字实际上是一对 (m,e),它表示的值 ff=mb^e (其中 b 是一个固定的整数基数,通常为 2)。因此,自然的分数表示形式是 m / b^(-e),其中 e<0 (如果 e>=0f 已经是整数了)。
  • 然而,您可能想获得 具有最小合理分母 的分数。这是一个不同的问题。要获得它,您可以使用 Pari/GP 库中的 bestappr 函数。在您的情况下,您可能会使用 bestappr(x, A),其中 x 是您的输入,A 是您想尝试的最大分母。bestappr 将为您提供最接近 x 且分母仍小于 A 的分数。

6

编写自己的Rational类来计算除法

class Rational
{
public:
    int numerator, denominator;

    Rational(int num, int den=1){
        numerator = num;
        denominator=den;
    }
    Rational(Rational other){
        numerator = other.numerator;
        denominator = other.denominator;
    }
    double operator / (int divisor){
            denominator *= divisor;
            simplificate();
            return getrealformat();
    }
    Rational& operator / (int divisor){
            denominator *= divisor;
            simplificate();
            return this;
    }
    Rational& operator / (Rational &divisor){
            numerator *= divisor.numerator;
            denominator *= divisor.denominator;
            simplificate();
            return this;
    }
    double operator / (int divisor){
            denominator *= divisor;
            simplificate();
        return getrealformat();
    }
    double getrealformat(){
        return numerator/denominator;
    }
    simplificate(){
        int commondivisor = 1;
        for(int i=2;i<=min(abs(numerator), abs(denominator));i++)
            if( numerator%i == 0 && denominator%i == 0 )
                commondivisor = i;
        numerator /= commondivisor;
        denominator /= commondivisor;
    }
};

使用

Rational r1(45), r2(90), r3=r1/r2;
cout<<r3.numerator<<'/'<<r3.denominator;
cout<<r3.getrealformat();

1
这个程序如何处理从 floatdouble 转换为 Rational 的过程? - Thomas Matthews
1
有理数 operator=(double number){ numerator = number*decimals;/之前已经指定,例如100/ denominator = decimals; 简化(); } - Aak
在这里,我尝试测试上述提到的类。然而,我无法直接使用上面的代码(因为它显示了一些错误),所以我不得不重写一些东西。最后它并没有按照预期工作。有人可以检查一下出了什么问题吗?@ThomasMatthews - user9267359
谢谢提供链接,但是防火墙阻止了访问该链接。也许您可以编辑您的帖子? - Thomas Matthews
@ThomasMatthews 我创建了一个新链接:https://paste.ofcode.org/SVZLDr72BUyEUkam5GrAEt 希望这个链接能够正常工作。 - user9267359

4

如何仅获取原始格式(即2/3),而不是0.66666667

只有通过使用类似GMP库的自定义输出运算符来包装才能实现。以下是有关GMP的更多信息:

GMP是什么?

GMP是一个自由的库,用于任意精度算术,可操作有符号整数、有理数和浮点数。除了机器上可用内存所暗示的限制外,精度没有实际限制。GMP具有丰富的功能,并且这些功能都具有规则的接口。

GMP的主要目标应用程序是密码学应用程序和研究、Internet安全应用程序、代数系统、计算代数研究等。

GMP被精心设计成尽可能快,对于小操作数和大操作数都是如此。通过使用fullwords作为基本算术类型,使用快速算法,对于许多CPU的最常见内部循环使用高度优化的汇编代码以及一般强调速度来实现速度。

GMP比任何其他bignum库都要快。对于许多操作,GMP的优势随着操作数大小的增加而增加,因为GMP使用渐近更快的算法。

第一个GMP版本发布于1991年。它正在持续开发和维护,每年发布一个新版本。


3
这种设施或类似设施基本上是唯一的途径。你仍然需要从一开始就进行跟踪。如果你只有0.6666667,你无法知道它是6666667/10000000还是2/3。 - T.E.D.

4
你需要将它们存储在一种带有两个整数字段的分数类中。当然,在输出之前,您必须简化该分数。
您可以开发自己的类或使用一些库,例如用于精确数学计算的CLN - 数字类库

一个包含两个整数字段的类:分子和分母,无法帮助将floatdouble转换为分数。使用Fraction类可以进行分数转换。 - Thomas Matthews
Thomas,我不理解其中的区别。然而,floatdouble在某种程度上也是分数。据我所理解,问题是如何从一开始就处理这个问题,解决方案是避免首先创建floatdouble变量。 - Mauro Vanetti

3

通常情况下这是不可能的:浮点数不精确并且无法保存足够的信息以完全重构分数。

然而,您可以编写一个函数,启发式地找到“最佳”逼近值,其中优先选择具有小分子和分母的分数,以及与浮点数几乎相同的值的分数。

如果您完全控制代码,则 Oli 的想法更好:首先不要丢弃信息。


3
那并不完全正确。如果你有一个明确的精度要求(比如0.00001),你可以将该数字乘以该精度的倒数,得到一个较大的分子和分母。此时你可以对分子和分母进行因式分解,然后开始删除公共因数,直到得到最小的分数,该分数可以产生一个浮点值,并且与原始浮点数的精度在你指定的范围内。 - BobG
你的意思是:并不是所有浮点数都是这样的。更准确地说,对于任何浮点数,存在可数无穷多个有理数,至少与其他浮点数一样接近它,尽管只有一个有理数与该浮点数完全相等。这样说更好吗? - Eamon Nerbonne
@BobG:那个算法通常不会找到“最优”的分数,因为手动选择的初始分母(例如你的例子中的1/0.00001)不能被最优除数整除(例如Sean的例子中的“3”)。无论如何,这听起来像是一个不同的问题... - Eamon Nerbonne
1
无论如何,实现一个找到最佳有理逼近的算法并非必要;这样的实现已经存在,例如在Pari/GP中(请参见我的回答)。 - sleske

2

您可以将所有分数的分子和分母存储为整数。整数在二进制中具有精确表示。


1
直到它们停止。存在最大整数,过了这个范围就必须使用某种 bignum 库。或者浮点数,这又把他带回了原问题。 - T.E.D.
@T.E.D.,整数溢出不是OP的问题。 - ThomasMcLeod
我认为1/10^20的精度对于几乎所有应用都足够了。 - flownt

2
为了简化工作,建议尽可能使用已知分母。
我正在处理一个应用程序,其中分数仅限于2的幂或使用3(三分之一)的分母。
我使用近似值(四舍五入到最接近的1.0/24.0)转换为这些分数。
如果没有一些限制,寻找分母可能会非常繁琐,并且会占用大量执行时间。

0
#include <iostream>
using namespace std;

int main() {
    int a,b,q,r;
    cin>>a>>b;//first number and second number
    q = a/b;
    r = a-q*b;
    cout<<q<<" "<<r<<" "<<"/"<<" "<<b<<"\n";
    return 0;
}

我刚刚通过 a/b 得到商,然后通过 a-q*b 得到余数。如果有任何建议,请提出。


0

我是初学者,我使用的方法可能不是正确的方式

#include <iostream>

using namespace std;
int main ()
{
  double a;
  double b;
  double c;

  cout << "first number: ";
  cin >> a;
  cout << "second number: ";
  cin >> b;

  c = a/b;
  cout << "result is: " << c << endl;

  if (b != 0) {
    if (a > 0) {
      if (c - (int)c > 0 && c - (int)c < 1)
        cout << "fraction: " << a << "/" << b;
    } else {
      if (c - (int)c < 0 && c - (int)c < 1)
        cout << "fraction: " << a << "/" << b;
    }
  }

  return 0;
}

我认为你回答了一个不同的问题。你的回答是关于将浮点数的整数部分与非整数部分分离开来(即将1.25分离成1.25)。但是这个问题是关于将浮点数转换为有理数的分数表示,即1 1/45/4 - jogojapan
通过这个方法,你可以识别浮点数的结果,并以分数的方式打印出来。 - EmPlusPlus
你的程序对于 1.25 的输出是 1.25,对吗?它是如何将 .25 转换为 1/4 的呢? - jogojapan
2
但是现在你的程序要求用户明确输入 ab。其他一些答案也提到了这一点;如果输入一个有理数,你可以简单地将其存储起来,以便在需要时使用。但真正的困难在于从浮点数计算 ab - jogojapan
我的意思是:尝试编写一个程序,用户仅输入 c,程序就会计算出 ab。但无论如何...这并不是很重要。 - jogojapan
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接