为什么没有实现理性数字并将其存储为零丢失信息的分数?

时间:2012-05-27 03:21:43

标签: c++ floating-point numbers

我知道这有点假设,但我想知道为什么我所知道的语言没有。

例如,您想存储1/3。给程序员一个选项,将其指定为1/3,并存储1和3.类似

struct float {
    int numerator;
    int denominator;
};

有理数运算变得非常容易且更准确!

这将解决与浮点数的精度和存储限制相关的许多问题,我也没有看到它引入任何新问题!

因此我的问题是:为什么理性数字不能实现并存储为零丢失信息?


正如Joe所说,其他人也可能会指出,我并不是要取代现有系统,而是要补充它。

问:你如何存储pi

答:很多次,我只是存储1/3而不是pipi可以旧方式存储,1/3以新方式存储。

8 个答案:

答案 0 :(得分:17)

默认情况下它们不以这种方式存储的原因是可以适合固定位组的有效值范围较小。您的float类可以存储1 / MAXINT和MAXINT(加或减)之间的数字。 C / C ++ float可以表示1E + 37和1E-37(加或减)之间的数字。换句话说,标准float可以表示比你的数量大26个数量级的值,比你的数量小一半的数量小26个数量级。通常,能够表示非常大且非常小的值比完全精确更方便。这尤其正确,因为舍入倾向于给出正确的答案,如1/3的小分数。在g ++中,以下给出1:

std::cout << ((1.0/3.0) * 3.0) << std::endl; 

请记住,C ++中的类型具有固定的位大小。因此,32位的数据类型最多具有MAX_UINT值。如果改变它的表示方式,你只需改变可以精确表示的值,而不是增加它们。你不能更多地填补,因此不能“更精确”。交易能够精确地表示1/3,因为无法准确表示其他值,如5.4235E + 25。

确实,float可以更精确地表示1E-9和1E + 9之间的值(假设32位整数),但代价是完全无法表示超出此范围的值。更糟糕的是,虽然标准float总是有6位精度,但float的精度会根据值的接近零而变化。 (请注意,您使用float所用位的两倍。)

(我假设32位int s。相同的参数适用于64位int s。)

修改:另请注意,人们使用float的大多数数据无论如何都不准确。如果您正在读取传感器的数据,那么您已经有了不精确,因此即将“完美地”表示该值是毫无意义的。如果您在任何类型的计算环境中使用float,那都无关紧要。如果您的目的是在屏幕的1/3处显示一些文本,那么完美地描述“1/3”是毫无意义的。

真正需要完美精确度的唯一人是数学家,他们通常都有软件给他们这个。很少有人需要超出double给出的精度。

答案 1 :(得分:9)

  

实数运算变得非常容易且准确得多!

不,它没有。您描述的结构只处理有理数字,即那些可以表示为分数的数字。实数集合包括理性数字和无理数字。大多数真实世界的计算是使用实数进行的,所以你不能仅限于理性,并期望一切都好。

  

我想知道为什么我所知道的语言没有。

我能想到的大多数语言都可以完全按照您的描述进行操作。在C中,您可以创建一个包含分子和分母的结构,并且可以定义一组对这些结构进行操作的函数。通过让你在该类上定义一个类和操作 - 相同的想法,更好的语法等等,C ++使事情变得更容易。事实上,不同的数字集通常被用作OO语言中的例子:你可以从定义一个Rational类,然后将其扩展为包含虚数,等等。

我猜想没有更多语言内置支持精确类型的原因可能与处理器不直接支持此类操作的事实有关。现代处理器包括实现浮点类型的算术运算的指令,因此很容易将它们包含在任何语言中。支持确切的类型意味着在语言中建立一个数学库,并且在几个层次上可能更好的方法是将数学库从语言中删除,让那些需要它的人将它构建到他们的软件中。

如果你要尽力避免产生精确的结果,你可能不希望仅限于理性,所以你给出的结构不会削减它。如果你在第一次看到不合理的数字时回到不精确的结果,那么能够对理性进行精确计算并不是很有帮助。幸运的是,那里有复杂的数学系统。 Mathematica是一个众所周知的例子。

答案 2 :(得分:8)

C ++至少包括一个编译时有理算术库。这是一个例子:

#include <ratio>
#include <iostream>

int main() {
    using a = std::ratio<3,5>;
    using b = std::ratio<7,6>;

    using c = std::ratio_multiply<a,b>::type;

    std::cout << c::num << '/' << c::den << '\n'; // prints 7/10
}

这里我们将3/5乘以7/6并得到7/10。

答案 3 :(得分:6)

戴上你的头盔,因为我们即将在这里理论化。

任何数学本科生都可以给你电梯解释康托尔对实数的无数基数的证明。如需更长的解释,请转到here.

但正如Caleb指出的那样,实数字段包含有理数和无理数。这意味着实数字段的某个子集永远不能表示为分子/分母对。这个子集有多大?事实证明,大多数实数是不合理的,因为这组理性是可数的。

这是一个妙语:以这种方式存储数字将非常愚蠢,因为实物值函数的大多数输出不能存储为分子和分母。

这似乎很难相信,但想想常见的先验功能,例如:罪,cos,日志。这些功能的大多数输出​​都不合理,编写IEEE 754和其他早期FP的人知道这一点。他们认为处理少量错误以换取代表(有些截断)实数字段的更大部分的可能性是一个很好的设计权衡。

答案 4 :(得分:5)

许多CPU对浮点(see Wikipedia)有特殊处理,语言中的float数据类型确保程序可以轻松地利用FPU。另一方面,我不知道任何可以使用特殊汇编程序指令处理分数的CPU,因此可以在库中轻松有效地实现分数,而不必是语言功能。如果要在C ++中使用分数,可以使用Boost.Rational

现代CPU实现浮点运算而不是处理分数的原因是浮点数可以更容易实现。要实现基本float操作,您基本上需要能够添加,减去乘法和除以整数并进行一些位移。另一方面,为了与分数进行比较,您需要找到两个整数的最大公约数,这在硬件中实现起来要困难得多。

答案 5 :(得分:4)

与您获得的许多答案相反,您的想法适用于多种问题。特别是problems like this one in which you know that there will be a lot of cancelling, and want an exact answer.因此,Python有fractions模块,而@ bames53指出,C ++有<ratio>

答案 6 :(得分:2)

  

“因此我的问题是:为什么没有实现理性数字并将其存储为零信息丢失的分数?”

C ++标准库缺少其他语言和库提供的许​​多实际必需的类型。 Boost库已经提供rational number implementation。并且appears它可能很快也会提供proposed decimal types,例如处理货币金额。

至于为什么缺少这些类型,C ++标准库通常只提供简单的一般构建块,而不是那些用这些块构建的更实用的东西。即这是简约的。 Boost库只是不那么简约,然后例如Poco库更像其他语言的功能更丰富的标准库。

答案 7 :(得分:1)

有几个原因:
- 没有共同架构的支持。这是可以理解的,因为分数必须始终简化。在硬件级别实现这一点并非易事,或者更确切地说,它将是一条没有太多应用的指令 - 无法处理非常大或非常小的数字。或BigInteger必须涉及。对于非常大或非常小的数字,我们通常不需要提供的大部分精度 - 如果这是语言级别支持的类型,则必须支持使用其他数字类型进行转换。如果内部表示具有固定的精度(在乘法的情况下),它必须决定何时返回浮点类型。

在某种语言中,支持某事的决定通常取决于其应用(或语言的基本原理)。如果应用程序很小,则支持的机会就会减少。