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

Laz*_*zer 27 c++ floating-point numbers

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

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

struct float {
    int numerator;
    int denominator;
};
Run Code Online (Sandbox Code Playgroud)

有理数运算变得非常简单,而且更准确!

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

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


正如乔问的那样,其他人也可能会指出,我并不是说要取代现有的系统,而是要补充它.

问:你如何储存pi

答:这么多次,我只是存储1/3而不是pi.pi可以以旧的方式存储,并1/3以新的方式存储.

Gor*_*bot 17

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

std::cout << ((1.0/3.0) * 3.0) << std::endl; 
Run Code Online (Sandbox Code Playgroud)

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

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

(我假设32位ints.相同的参数适用于64位ints.)

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

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

  • 你提到数学家是唯一的反例,一个永远不应该触及浮点数的重要领域是财务应用程序(AFAIK他们有自己的舍入规则,不适用于浮点数). (3认同)

Cal*_*leb 9

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

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

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

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

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

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

  • 我会说这应该是一个评论,而不是答案,因为对OP的一个小的,单词的修改它再次成为一个有效的问题(即:实数 - >有理数). (4认同)
  • 虽然实数集包括有理数和无理数,但标准数据类型只能处理有理数."浮点数"在内部有两个值,一个积分分子和十分之一的分母.因此,它总是理性的. (3认同)
  • 但是,该提案也可以这样说.355/113是pi的有理逼近,因为31415926/10000000.两者都是"实数的有限精度表示". (2认同)

bam*_*s53 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
}
Run Code Online (Sandbox Code Playgroud)

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


pg1*_*989 6

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

任何数学本科生都可以给你电梯解释Cantor证明实数的无数基数.有关更长的解释,请转到此处.

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

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

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

  • 由于无理数不能用有限的十进制(或二进制)表示来表示([见维基百科](http://en.wikipedia.org/wiki/Irrational_number#Decimal_expansions)),实际上是一个(IEEE 754)`浮点数'.可以代表没有无理的数字. (2认同)
  • 部分是的.正如我之前所说的,一些非常基本的理论论证表明,它是一个非常糟糕的工程决策的典型例子,即因为在狭窄的用例中,这是一项大量的时间和精力投入. (2认同)
  • 您的穿孔线可以修改为适用于IEEE 754值."以这种方式存储数字将非常愚蠢,因为实值函数的大多数输出​​不能存储为指数和有限精度分数." 我相信这表明你的结论不符合命题.使用分子/分母对可能很愚蠢,但不是出于这个原因. (2认同)

Flo*_*ade 5

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

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

  • 有一项提案:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3363.html (5认同)