c++如何在大浮点数上定义?< / h1 >

How is ++ defined on a large floating point

本文关键字:lt gt h1 定义 浮点数 c++      更新时间:2023-10-16

所以我一直在看IEEE754浮点双精度。(我的c++编译器使用该类型的double)。

考虑下面的代码片段:

// 9007199254740992 is the 53rd power of 2.
// 590295810358705700000 is the 69th power of 2.
for (double f = 9007199254740992; f <= 590295810358705700000; ++f){
/* what is f?*/
}

大概f的偶数阶递增到2的54次方,由于四舍五入?

之后,由于四舍五入,什么都没有发生?

正确吗?它有很好的定义吗?

++f本质上与f = f + 1相同,忽略了++f是产生值的表达式这一事实。

现在,对于浮点值,可表示性的问题开始发挥作用。f + 1可能是不可表示的。在这种情况下,f + 1将计算出最接近f + 1真实值的可表示值。如果最接近的可表示值有两个同样接近的候选值,则使用四舍五入到偶数。

这在《每个计算机科学家都应该知道的浮点算术》的操作部分中有介绍:

IEEE标准要求加、减、乘、除运算的结果必须精确舍入。也就是说,必须精确计算结果,然后四舍五入到最接近的浮点数(使用四舍五入为偶数)。

所以,如果你的例子中,f的值足够大,你会发现f == f + 1.

是的,这个循环将永远不会在舍入问题结束。我希望原因对你来说很清楚(因为你熟悉https://en.wikipedia.org/wiki/IEEE_floating_point),但让我简短地描述一下,以防不耐烦的观众。

我们可以认为浮点数是由编译器/FPU/标准数字的特殊表示所强制的。举个简单的例子:

  • 20000
  • 2 e4
  • 0.2 e5

这三种形式表示相同的数字。最后两种形式称为"科学"形式,但哪种形式最好?IEEE754答案-最后一个,因为我们可以通过省略前导0来节省空间写下.2e5。这种十进制类比非常接近二进制表示,其中有一个尾数(.2)和指数(5)的空间。

现在让我们对20000.00000000001

做同样的操作0.2000000000000001e5

正如我们所看到的尾数增长和固定内存溢出的一些限制。为了避免异常,我们牺牲了精度,如0.2e5.

对于更大的数(如问题),我们也失去了精度。

9007199254740992可以表示为0.9e16,加上1后没有任何变化。因此,f = f + 1创建了无限循环

与f = f+1相同,正如评论中指出的那样,并且正如我自己测试的那样,f == f+1(!!)对于依赖于平台的大f。这里有一个解释(对于小数字,但原理是一样的)http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BinMath/addFloat.html

下面是如何添加浮点数。

首先,将这两种表示转换为科学记数法。因此,我们显式地表示隐藏的1。为了加,我们需要这两个数的指数必须相同。我们重写一下这将导致Y没有被标准化,但值是相等的y的指数加上x - y。移动基点x - Y留下的尾数(有效的)Y来补偿指数变化。将X的两个尾数和调整后的Y相加在一起。如果前一步中的和没有一个位值1的,在基数点的左边,然后调整基数点和幂,直到它等于。转换回一个字节的浮点数表示。

在将数转换为相同指数的过程中,出于精度考虑,将1四舍五入为0,因此f == f + 1。

根据IEEE754,和后的数字四舍五入以匹配双格式,由于四舍五入操作,f==f+1。

我不知道是否存在这样的问题,即通过增加1来循环大浮点值是一个有意义的解决方案,但人们可能会在这个问题上绊倒,寻找他们无休止循环的解决方案。因此,尽管这个问题只问标准如何定义加法,我还是会提出一个解决方案。

实际上,对于f的较大值,f++ == f为真,并且使用它作为循环中的增量将具有未定义的行为。

假设f加一个比1大的最小的数字e,其中浮点数的表示形式为f + e > f。在这种情况下,以下循环将始终终止的解决方案可以是OK的:

// use template, or overloads for different floatingpoints
template<class T>
T add_s(T l, T r) {
T result = l + r;
T greater = std::max(l, r);
if(result == greater)
return std::nextafter(greater, std::numeric_limits<T>::max());
return result;
}
// ...
for (double f = /*...*/; f < /*...*/; f = add_s(f, 1.0))

也就是说,将微小的浮点数添加到巨大的浮点数上将导致错误的不可控制的累积。如果这对你来说不合适,那么你需要任意精度的数学,而不是浮点。