在for循环中使用科学记数法

我最近遇到了一些代码,它有一个循环的forms

for (int i = 0; i < 1e7; i++){ } 

我怀疑这样做的智慧,因为1e7是一个浮点types,在评估停止条件时会导致i被提升。 这是否应该引起关注?

这里的大象是一个int的范围可以小到-32767到+32767,并且对这个int分配比这个更大的值的行为是未定义的

但是,至于你的主要观点,确实应该关心你,因为这是一个非常不好的习惯。 事情可能会出错是,1e7是浮点双精度型。

由于types提升规则, i将被转换为浮点的事实是有点没有实际意义的:真正的损害是完成的,如果有意外的整数文字截断 。 通过“以身作则”的方式,首先考虑循环

 for (std::uint64_t i = std::numeric_limits<std::uint64_t>::max() - 1024; i ++< 18446744073709551615ULL; ){ std::cout << i << "\n"; } 

如你所期望的那样,这将输出范围内每个连续的i值。 请注意, std::numeric_limits<std::uint64_t>::max()18446744073709551615ULL ,它比2的64次幂小1(这里我使用了一个幻灯片式的“运算符” ++<当使用unsignedtypes时,许多人认为-->++<是混淆的,但在科学编程中它们是常见的,尤其是--> 。)

现在在我的机器上,double是一个IEEE754 64位浮点。 (比如scheme特别擅长表示2的幂 – IEEE754可以表示2到1022的幂)。因此, 18,446,744,073,709,551,616 (2的64次幂)可以精确地表示为双精度。 最近的可表示的数字是18,446,744,073,709,550,592 (less了1024)。

所以现在让我们把循环写成

 for (std::uint64_t i = std::numeric_limits<std::uint64_t>::max() - 1024; i ++< 1.8446744073709551615e19; ){ std::cout << i << "\n"; } 

在我的机器上,只会输出i18,446,744,073,709,550,592 (我们已经看到的数字)的一个值。 这certificate1.8446744073709551615e19是一个浮点types。 如果允许编译器将字面量视为整型,那么两个循环的输出将是等效的。

它会工作,假设你的int至less是32位。

但是,如果你真的想要使用指数表示法,你最好在循环外定义一个整型常量并使用适当的转换,如下所示:

 const int MAX_INDEX = static_cast<int>(1.0e7); ... for (int i = 0; i < MAX_INDEX; i++) { ... } 

考虑到这一点,我可以说写得好多了

 const int MAX_INDEX = 10000000; 

或者如果你可以使用C ++ 14

 const int MAX_INDEX = 10'000'000; 

1e7doubletypes的文字,通常是64位IEEE 754格式,带有52位尾数。 2的大约十分之一幂对应于10的三次方,所以double应该能够精确地表示至less10 5 * 3 = 10 15的整数。 如果int是32位,那么int最大值大约是10 3 * 3 = 10 9 (要求谷歌search它说“2 ** 31 – 1”= 2 147 483 647,即粗略估计值的两倍)。

所以,实际上在当前的桌面系统和更大的系统上是安全的。

但是C ++允许int只有16位,例如在一个embedded式系统中,这个int就会有一个Undefined Behavior。

如果意图循环一个精确的整数迭代次数,例如,如果对一个数组中的所有元素进行迭代,那么与浮点值进行比较可能不是一个好主意,仅仅出于准确性原因; 因为将整数隐式转换为float会将整数截断为零,所以不存在实际出现越界访问的危险,因此它将仅放弃短循环。

现在的问题是:什么时候这些效应实际上踢了? 你的程序会经历他们吗? 目前通常使用的浮点表示是IEEE 754.只要指数是0,浮点值本质上是一个整数。 C双精度浮点数52位为尾数,它给你整数精度的值达2 ^ 52,大约在1e15。 如果没有用后缀f来指定你想要一个浮点文字被解释为单精度,那么文字将是双精度的,而隐式转换也将以此为目标。 所以只要你的循环结束条件less于2 ^ 52就可以可靠地工作!

现在有一个关于x86架构的问题是效率问题。 最初的80×87 FPU采用了不同的封装,后来又有了一个不同的芯片,而且在x86汇编级别上,获得值进入FPU寄存器的时间有些尴尬。 取决于你的意图是什么,它可能会在实时应用程序运行时的差异; 但这是不成熟的优化。

TL; DR:安全吗? 当然是的。 会造成麻烦吗? 这可能会导致数字问题。 它可以调用未定义的行为? 取决于你如何使用循环结束条件,但如果i用于索引数组,并由于某种原因数组长度结束在一个浮点variables总是截断为零这不会导致逻辑问题。 这是一个聪明的事情吗? 取决于应用程序。