static_cast
从浮点到整数只是删除数字的小数点。例如,static_cast<int>(13.9999999)
会产生13
。
并非所有整数都可以表示为浮点数。例如,内部最接近的float
到13,000,000可能是:12999999.999999
。
在这个假设的情况下,我希望得到一个意想不到的结果:
const auto foo = 12'999'999.5F;
const auto bar = static_cast<long long>(ceil(foo));
我的假设是,如果不一定是13,000,000,这种故障确实会在某个时刻发生。我只想知道我可以信任的范围static_cast<long long>(ceif(foo))
?
答案 0 :(得分:1)
例如,在内部,最接近13,000,000的浮点数可能是:12999999.999999。
在任何正常的浮点格式中都不可能。数字的浮点表示等同于 M • b e ,其中 b 是一个固定的基数(例如,2表示二进制浮点数), M 和 e 是整数,对它们的值有一些限制。为了表示像13,000,000- x 这样的值,其中 x 是一个小于1的正值, e 必须是负数(因为 M • b e 对于非负 e 是整数)。如果是,那么 M •b 0 是一个大于 M 的整数• b e ,因此大于13,000,000,因此13,000,000可以表示为 M&#39; • b 0 ,其中 M&#39; 是一个小于 M 的正整数,因此符合 M 的允许值范围(在任何正常浮点格式)。 (也许一些奇怪的浮点格式可能会在 M 或 e 上施加一个奇怪的范围来阻止这种情况,但是没有正常的格式。)
关于您的代码:
auto test = 0LL;
const auto floater = 0.5F;
for(auto i = 0LL; i == test; i = std::ceil(i + floater)) ++test;
cout << test << endl;
当i
为8,388,608时,8,388,608 + .5的数学结果为8,388,608.5。这在您的系统上以float
格式无法表示,因此它被舍入为8,388,608。 ceil
这是8,388,608。此时,test
为8,388,609,因此循环停止。所以这段代码并没有证明8,388,608.5是可表示的,8,388,609不是。
如果我这样做,行为似乎会恢复正常:ceil(8&#39; 388&#39; 609.5F)将正确返回8,388,610。
8,388,609.5在您的系统上以float
格式无法表示,因此它被“舍入为最接近,与均匀关系”的规则四舍五入。两个最接近的可表示值为8,388,609和8,388,610。由于它们相距很远,结果是8,388,610。该值传递给ceil
,当然返回8,388,610。
在Visual Studio 2015上,我得到了8,388,609,这是一个可怕的小安全范围。
在IEEE-754基本32位二进制格式中,从-16,777,216到+16,777,216的所有整数都是可表示的,因为格式具有24位有效数字。
答案 1 :(得分:0)
Floating point numbers由3个整数, cb q 表示:
由此可以很容易地看出,具有表示 12,999,999.999999 的浮点数也能够使用 c 表示 13,000,000.000000 1,300,000,000,000 和 q -5 。
这个例子有点人为,因为所选择的 b 是 10 ,其中几乎所有实现中所选择的基数都是 2 。但值得指出的是,即使 b 2 , q 也可以作为尾数的左移或右移。
接下来让我们来谈谈一个范围。显然,32位浮点不能表示由32位整数表示的所有整数,因为浮点数也必须代表更多或更多的数字。由于指数只是简单地移动尾数,浮点数总是完全表示可以用它的尾数表示的每个整数。鉴于传统的IEEE-754二进制基础浮点数:
float
)有一个24位尾数,因此它可以表示[ -16,777,215 , 16,777,215 ] <范围内的所有整数/ LI>
double
)有一个53位的尾数,因此它可以表示[ -9,007,199,254,740,991 , 9,007,199,254,740,991 ] <范围内的所有整数/ LI>
long double
取决于实现)具有113位尾数,因此它可以表示范围内的所有整数[ -103,845,937,170,696,552,570,609,926,584,40,191 , 103,845,937,170,696,552,570,609,926,584, 40191 [source]
c++提供digits
作为查找给定浮点类型的此数字的方法。 (尽管可以肯定,即使long long
太小也不能代表113位尾数。)例如,float
的最大尾数可以通过以下方式找到:
(1LL << numeric_limits<float>::digits) - 1LL
彻底解释了尾数后,让我们重温指数部分来讨论floating point is actually stored的方式。取 13,000,000.0 ,可以表示为:
等等。对于传统的二进制格式IEEE-754要求:
通过选择保留所选字大小和格式中最高有效位(MSB)的最小可表示指数,使表示唯一。此外,指数不是直接表示,而是添加偏差,使得最小的可表示指数表示为1,其中0表示次正规数
如果我们的尾数有 14 小数位,在更熟悉的base-10中解释这一点,实现将如下所示:
转换回base-2这意味着numeric_limits<T>::digits - 1
的 q 在小数位后只有零。 ceil
只有在数字的一小部分时才有效。
这里的最后一点解释是ceil
将产生影响的范围。在浮点的指数大于numeric_limits<T>::digits
继续增加之后,它仅向结果数引入尾随零,因此当 q 大于或等于时,调用ceil
numeric_limits<T>::digits - 2LL
。由于我们知道 c 的MSB将在数字中使用,这意味着 c 必须小于(1LL << numeric_limits<T>::digits - 1LL) - 1LL
因此对于ceil
来说对传统二进制IEEE-754浮点的影响:
float
)必须小于 8,388,607 double
)必须小于 4,503,599,627,370,495 long double
取决于实施)必须小于 5,192,296,858,534,827,628,530,496,329,220,095