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

时间:2016-06-20 08:31:59

标签: c++ loops

我最近遇到了一些代码,其中包含

形式的循环
for (int i = 0; i < 1e7; i++){
}

我质疑这样做的智慧,因为1e7是浮点类型,并且在评估停止条件时会促使i被提升。这应该引起关注吗?

4 个答案:

答案 0 :(得分:50)

这里的大象int 的范围小到-32767到+32767,以及分配时的行为这个int的值大于此值 undefined

但是,至于你的要点,确实应该关注你,因为这是一个非常坏习惯。事情可能会出错,1e7是浮点式双重类型。

由于类型提升规则将i转换为浮点这一事实有点没有实际意义:如果存在明显的整数文字的意外截断,则会造成真正的损害。通过&#34;示例证明&#34;,首先考虑循环

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。(此处我使用类似幻灯片的&#34;运算符&#34; ++<这在使用unsigned类型时非常有用。许多人认为-->++<是混淆的,但在科学编程中它们很常见,尤其是-->。)

现在在我的机器上,double是一个IEEE754 64位浮点。 (例如,方案特别擅长于精确表示2的幂 - IEEE754可以精确地表示2到1022的幂。)因此18,446,744,073,709,551,616(2的64次幂)可以完全表示为double。之前最接近的可表示数字是18,446,744,073,709,550,592(减少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(我们已经看过的数字)。这证明1.8446744073709551615e19是浮点类型。如果允许编译器将文字视为整数类型,则两个循环的输出将是等效的。

答案 1 :(得分:14)

假设您的int至少为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;

答案 2 :(得分:10)

1e7double类型的文字,通常double是64位IEEE 754格式,带有52位尾数。大约每2的十次幂对应于10的三次幂,所以double应该能够表示至少10 5 * 3 = 10 15 ,完全。如果int是32位,那么int大约有10 3 * 3 = 10 9 作为最大值(要求Google搜索它说&#34; 2 ** 31 - 1&#34; = 2 147 483 647,即粗略估计的两倍。

因此,在实践中,它在当前的桌面系统上更安全。

但C ++允许int仅为16位,例如一个具有小int的嵌入式系统,其中一个将具有未定义的行为。

答案 3 :(得分:2)

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

现在问题是:这些影响何时开始?你的程序会体验它们吗?目前通常使用的浮点表示是IEEE 754.只要指数为0,浮点值基本上就是整数。对于尾数,C双精度浮点数为52位,这为整数精度提供了最多2 ^ 52的值,大约为1e15。如果不指定后缀f,您希望浮点文字被解释为单精度,则文字将是双精度,隐式转换也将以此为目标。因此,只要您的循环结束条件少于2 ^ 52,它就可以可靠

现在,您需要考虑x86架构的一个问题是效率。最初的80x87 FPU采用不同的封装,后来又采用了不同的芯片,因此在x86汇编级别上获取值进入FPU寄存器有点尴尬。根据您的意图,它可能会影响实时应用程序的运行时间;但那是不成熟的优化。

TL; DR:安全吗?肯定是的。它会引起麻烦吗?它可能会导致数值问题。它可以调用未定义的行为吗?取决于你如何使用循环结束条件,但如果i用于索引数组,并且由于某种原因,数组长度最终在浮点变量中总是截断为零,则不会导致逻辑问题。这样做很聪明吗?取决于应用程序。