从long long到float的隐式转换会产生意外结果

时间:2017-01-04 02:32:15

标签: c++

试图验证(使用VS2012)一本书的索赔(第二句)

When we assign an integral value to an object of floating-point type, the fractional part is zero. 
Precision may be lost if the integer has more bits than the floating-point object can accommodate.

我写了以下内容:

#include <iostream>
#include <iomanip>

using std::cout;
using std::setprecision;

int main()
{
    long long i = 4611686018427387905; // 2^62 + 2^0

    float f = i; 

    std::streamsize prec = cout.precision();

    cout << i << " " << setprecision(20) << f << setprecision(prec) << std::endl;

    return 0;
}

输出

4611686018427387905 4611686018427387900

我期待表格的输出

4611686018427387905 4611690000000000000

4字节的浮点数如何能够保留8字节整数的大量信息?是否有实际证明索赔的i值?

2 个答案:

答案 0 :(得分:5)

浮点数不会将数据存储在基数10中,而是将它存储在基数2中。因此,4611690000000000000实际上并不是一个非常圆的数字。它的二进制表示是:

100000000000000000000111001111100001000001110001010000000000000. 

正如您所看到的,这需要大量数据才能准确记录。但是,实际打印的数字具有以下二进制表示形式:

11111111111111111111111111111111111111111111111111111111111100

正如你所看到的那样,这是一个更加圆润的数字,并且从2的幂中减去4的事实可能是由于转换为base-10算法的四舍五入。

作为不能正确适合浮动的数字的示例,请尝试您期望的数字:

4611690000000000000

你会发现情况会有很大不同。

答案 1 :(得分:4)

浮动保留了如此多的信息,因为你正在使用一个非常接近2的幂的数字。

浮点格式以基本二进制科学记数法存储数字。在您的情况下,它存储为类似

的内容

1.0000000 ... [61 zeroes] ... 00000001 * 2 ^ 62。

浮动格式不能存储62个小数位,所以最后1个会被截断...但是我们留下了2 ^ 62,这几乎完全等于你的数字#3;重新尝试存储。

我在制造示例方面表现不佳,但CERT不是;您可以查看错误的号码转化here会发生什么情况的示例。请注意,该示例使用的是Java,但C ++使用相同的浮点类型;另外,第一个例子是4字节int和4字节float之间的转换,但这进一步证明了你的观点(需要存储的整数信息少于在你的例子中,它仍然失败了。)