浮点数的一些认识
浮点数的一些认识
浮点数包括 float 和 double 两种类型, float 占 32 位, double 占 64 位。其二进制存储格式遵循 IEEE754 标准。以 float 为例:
符号位:正数为 0 ,负数为 1
以 float 型数据 123.456 为例,分析其二进制存储格式:
首先将十进制数 123.456 转换为二进制数为: 1111011. 01110100101111001
(其中 0.456 如何转换为二进制?不断乘以 2… )
1111011. 01110100101111001 即 1. 11101101110100101111001 乘以 2 的 6 次方
首先这是一个正数,则符号位为 0
阶码为 6 ,不过要转换成移码。
(如何求 6 的移码?这里我也不太深究,我见大家都是直接 6+127=133 ,换为 2 进制为 10000101 )
尾数则为 1. 11101101110100101111001 的小数部分,即
11101101110100101111001
综上: 123.456 的二进制存储格式为: 0 1000010 111101101110100101111001
用一段代码来验证一下:
#include <cstdlib>
#include <iostream>
using namespace std;
void printBinary(const unsigned char val)
{
for(int i = 7; i >= 0; i--)
if(val & (1 << i))
std::cout << "1";
else
std::cout << "0";
}
int main()
{
float d = 123.456;
unsigned char* cp = reinterpret_cast<unsigned char*>(&d);
for(int i = sizeof(float)-1; i >= 0 ; --i)
{
printBinary(cp[i]);
}
system("PAUSE");
}
要注意的是, X86 架构为小端模式,是指数据的低位保存在内存的低 地址 中,而数 据的高位保存在内存的高地址中。所以上面的 for(int i = sizeof(float)-1; i >= 0 ; --i) 先打印高地址部分,即二进制的高字节数据。
程序的执行结果:
0 1000010 111101101110100101111001
对比一下刚才的分析结果是相同的。
double 类型和 float 类型的二进制存储格式是同样的道理。
以上部分是我从别人的博客中转来的,大家可以看看,好了,下面我提出一个问题,
double a;
a=1.1;
int b = (int&) a;
然后输出b,会是什么结果,1吗,当然不是,结果是 -1717986918,为什么呢,我们来分析一下:
double是用52位来存储小数部分的,我们算出52位的存储应当是 0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010
最后四位需要注意,因为在计算机里面也是需要“四舍五入的”。如果省略的后一位是1那么前一位就要加一来存储。所以1001 + 1就是1010。
四、取最后面的32位,换成有符号的十进制。第一位是符号,表示负数,取补码就是 0110 0110 0110 0110 0110 0110 0110 0110就是1717986918。即是-号的这个数而已。
另外还有一个很有意思的事情,看看下面的代码:
double a=3.0,b=10.0,ans;
ans=a/b;
单步调试看看ans的值会是多少,0.333333333...吗,当然不是,而是0.299999999...,为什么呢,分析一下,C语言里用的是IEEE794浮点数,比如double型是64位的。很多数,比如0.3用二进制表示是无限循环小数,自然会被截断。除非你用第三方的精确浮点库,否则只要是C语言,无论哪个编译器都是这个结果,你输出的时候最好用%5.2f这样的宽度控制来限制输出尾数,而且如果判断的话,一定不要直接用==。最好是abs(a-b)<0.000001之类的。
另外一个很好的方法是在3.0上面加一个无穷小的小数,比如你的编译器支持的浮点数的最小正小数是0.000000001,那么在运算的时候在3.0上加上这个值,再除10,就不会出现这样的情况了。这个只能解决部分问题,如果要心里安稳点的话,就看看标准库里的float.h头文件或者cfloat头文件吧
有个朋友说,最初的时候Excel和Windows计算器都用的IEEE794浮点数,double的有效数字最大就15位。
不过后来Windows计算器内部实现了一个比较复杂的模拟分数算法。你也可以用一对p和q保存值的分子和分母,认为遇到的值都是有理分数(无理数只能截断)。作为参考吧