对本周大师的理解#67:双重或无结果

tao*_*ocp 8 c++ floating-point precision gotw

最近,我正在阅读帖子:Herb Sutter对GOTW的双重或无效 我对以下程序的解释有点困惑:

 int main()
 {
     double x = 1e8;
     while( x > 0 )
     {
        --x;
     }
 }
Run Code Online (Sandbox Code Playgroud)

假设此代码在某台机器上运行1秒.我同意这样的代码是愚蠢的.

然而,每样,如果我们改变问题的解释xfloatdouble,那么在某些编译器,它会继续运行永远计算机.该解释基于该标准的以下引用.

引用C++标准的3.9.1/8节:

有三种浮点类型:float,double和long double.double类型提供至少与float一样多的精度,long double类型提供至少与double一样多的精度.float类型的值集是double类型的值集的子集; double类型的值集是long double类型的值集的子集.

代码的问题是:

如果你将"double"改为"float",你会期望它需要多长时间?为什么?

这是给出的解释:

它可能需要大约1秒(在特定实现上,浮点数可能稍微快一些,或者比双精度快一些,或者稍微慢一些),或者永远,这取决于浮点数是否可以精确地表示从0到1e8(包括0和1e8)的所有整数值.

标准的上述引用意味着可能存在可以用double表示但不能用float表示的值.特别是,在一些流行的平台和编译器中,double可以精确地表示[0,1e8]中的所有整数值,但float不能.

如果float不能准确表示0到1e8之间的所有整数值,该怎么办?然后修改后的程序将开始倒计时,但最终会达到无法表示的值N和N-1 == N(由于浮点精度不足)......

我的问题是:

如果float甚至不能表示1e8,那么当我们初始化时我们应该已经溢出float x = 1e8; 那怎么会让电脑永远运转呢?

我在这里尝试了一个简单的例子(虽然不是double但是int)

#include <iostream>

int main()
{
   int a = 4444444444444444444;
   std::cout << "a " << a << std::endl;
   return 0;
}
It outputs: a -1357789412
Run Code Online (Sandbox Code Playgroud)

这意味着如果编译器无法用int类型表示给定的数字,则会导致溢出.

所以我误读了吗?我错过了什么?是x从改变doublefloat未定义的行为?

谢谢!

Dan*_*her 9

关键词是"确切地".

float1e8甚至可以表示,除非你有一个怪胎float类型.但这并不意味着它可以精确地表示所有较小的值,例如,通常2^25+1 = 33554433需要26位精度,不能精确表示float(通常,具有23 + 1位精度),也不能2^25-1 = 33554431,这需要25精确度.

然后将这两个数字表示为2^25 = 33554432,然后

33554432.0f - 1 == 33554432.0f
Run Code Online (Sandbox Code Playgroud)

会循环.(你会先点击一个循环,但那个有一个很好的十进制表示;)

在整数运算中,您可以x - 1 != x使用全部算法,x但不能使用浮点运算.

注意,循环也可能完成即使float仅具有的精度通常的23 + 1个比特,由于标准允许浮点计算在比类型具有更高的精度来进行,并且如果计算是在足够更大的精度进行(例如通常double使用52 + 1位),每次减法都会改变x.

  • @taocp初始化时没有溢出.浮点格式持续降低*精度*,因为你达到更高的值,直到达到+ 1.#INF.无限运算&#NAN是合法的,而不是未定义的.但他们可能会有意想不到的结果 看一下[IEEE-754标准](https://en.wikipedia.org/wiki/IEEE_floating_point) (2认同)