小数在内存中是怎样存放的?

yichangwu1234 2009-05-06 05:45:48
说明白一点.
...全文
1358 10 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
FAR 2012-10-04
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 的回复:]

定点和浮点么,组成原理里都有的。要不要看ppt啊?我这里很多讲解的
[/Quote]你好能发我一份吗;farforever@farforever.com
谢了先。。。Q_Q
yichangwu1234 2009-05-07
  • 打赏
  • 举报
回复
一、浮点数的概念引入

在计算机中的数据有定点数和浮点数两种表示方式。

1. 定点数是指小数点固定在某个位置上的数据,一般有小数和整数两种表示形式。

定点小数:将小数点固定在数据数值部分的左边,符号位的右边。

定点整数:将小数点固定在数据数值部分的右边。

定点数表示法简单直观,但数值表示范围小,运算时容易产生溢出。



2. 为弥补定点数表示范围的不足,防止溢出,发明了浮点表示法。

浮点数是指小数点位置可浮动的数据。

浮点数标准是采用的IEEE标准754,是一种类似于科学计数法的表示法。



3. 科学计数法

任何一个数可以通过科学计数法表示为



其中:

1≤ <10,即不论正负数,a的整数部分只能有一位。(这和浮点数的表示方法一致)

n为整数。

10为基数,为约定值。

Eg:-12345.69的科学计数法表示过程为:

由于1≤ <10,所以小数点必须向左移动4位,即a=-1.234569,n=+4

所以其科学计数法表示形式为-1.234569×



同理,0.0001234569的科学计数法表示过程:

小数点必须向右移动4位,即a=1.234569,n=-4

所以其科学计数法表示形式为 1.234569×

4、移码的概念

当阶码为n+1位时,其最高位为符号位,

(X为原码)

注意这个和IEEE中的偏码不一样。

移码其实就是将补码的符号位取反,这样使得浮点数的机器0(当数据小于机器所能表示的最小数时≤- )为全0。

而数据0的表示为

二、IEEE标准754

1. IEEE标准754中规定了三种浮点数格式:单精度、双精度、扩展精度。此处以单精度为例。

单精度浮点数N是一个32位的由0或1组成的位序列,其格式图如下:

S
E
M


其中:

S:为符号位

E:为指数位,也称阶码,用移码表示。指明小数点在浮点数中的范围。决定了浮点数的表示范围。

M:为尾数位。决定了浮点数的精度。

若以s,e,m,n分别表示S,E,M,N的实际数值,则



2. e和m的计算

M

如同科学计数法一样,m的整数位也只能有一位,所以m只能为1或0,绝大多数情况下,都是为1,除非n值非常小。既然绝大多数情况下为1,那么该位即可省略,而且扩大了精度,一举两得。所以M的位序列只可能为1.0001011或0.0110100,如果m的整数位不是一位,则需要左右移动小数点并同时改变阶码值以达到这一要求的形式,这也称为浮点数的格式化表示。

特殊情况:①当一个浮点数的尾数为0时,不论其阶码为何值

②当阶码的值遇到比它能表示的最小值还小时,不管其尾数值为何值

这两种情况都把该浮点数视为0,称为机器0。

就是这个数实在是太小了,小数点一直往右移也一直没碰到1,直到达到了阶码能表示的最小值,这样的数则认为是0。

E

单精度浮点数E的位数为8位,除去阶符,其能表达的范围为








例:

将十进制浮点数-321.456转换成二进制表示。

① 负数,所以s=1

② 将整数部分321转换为二进制101000001,小数部分0.456转换为二进制,即乘上2,若进位则置1。转着转着发现一直难以达到1,这便是浮点数的精度问题,不管他,取15位0. 011101001011110,和整数部分合在一起凑成24位:

101000001.011101001011110,进行格式化,将小数点左移8位,则|E|=127+8=135

E=10000111,M=01000001011101001011110,S=1

③ 最后将三者合在一起,得到浮点数的二进制表示

1100 0011 1010 0000 1011 1010 0101 1110

化作16进制为C3 A0 BA 5E



倒过来,已知一个位序列,求出对应的十进制数。

已知位序列为1100 0011 1010 0000 1011 1010 0101 1110

可确定s=1

e=(100 0011 1)-127=8

m=|1. 010 0000 1011 1010 0101 1110|=1+2^(-2)+ 2^(-8)+ 2^(-10)+ 2^(-11)+ 2^(-12)+ 2^(-14)+ 2^(-17)+ 2^(-19)+ 2^(-20)+ 2^(-21)+ 2^(-22)

=1+0.25+0.0000002384185791015625(1+2+4+8+32+256+1024+2048+4096+16384)

=1.255687

所以n=(-1)^1+1.255687*2^8=321.45599
谢大家.
找了个比较完整的.
wocow3 2009-05-06
  • 打赏
  • 举报
回复
著名的IEEE754,google一下
xrloyx 2009-05-06
  • 打赏
  • 举报
回复
学过 但不是很明白
biweilun 2009-05-06
  • 打赏
  • 举报
回复
定点和浮点么,组成原理里都有的。要不要看ppt啊?我这里很多讲解的
码侬 2009-05-06
  • 打赏
  • 举报
回复
分浮点和定点两种
平时使用的float 和 double就是浮点

分为符号、底数和幂数三段

可以用下面代码察看内存格式:

float f = 100.00;
DWORD dw1;
memcpy(&dw1, &f, 4);

cstring str;
str.format("%x", dw1);

messagebox(str);
Mushu 2009-05-06
  • 打赏
  • 举报
回复
IEEE浮点标准
深入理解计算机系统P69
google结果
http://hotpower.21ic.org/user1/3873/archives/2007/40245.html
Allen_zhang 2009-05-06
  • 打赏
  • 举报
回复
自己看一下不就知道了

我记得计算机组成原理的时候有讲过的
路人乙2019 2009-05-06
  • 打赏
  • 举报
回复
浮点数是属于有理数中某特定子集的数的数字表示,在计算机中用以近似表示任意某个实数。具体的说,这个实数由一个整数或定点数(即尾数)乘以某个基数(计算机中通常是2)的整数次幂得到,这种表示方法类似于基数为10的科学记数法。
浮点计算是指浮点数参与的运算,这种运算通常伴随着因为无法精确表示而进行的近似或舍入。
一个浮点数a由两个数m和e来表示:a = m × b^e。在任意一个这样的系统中,我们选择一个基数b(记数系统的基)和精度p(即使用多少位来存储)。m(即尾数)是形如±d.ddd...ddd的p位数(每一位是一个介于0到b-1之间的整数,包括0和b-1)。如果m的第一位是非0整数,m称作规格化的。有一些描述使用一个单独的符号位(s 代表+或者-)来表示正负,这样m必须是正的。e是指数。
这种设计可以在某个固定长度的存储空间内表示定点数无法表示的更大范围的数。
例如,一个指数范围为±4的4位十进制浮点数可以用来表示43210,4.321或0.0004321,但是没有足够的精度来表示432.123和43212.3(必须近似为432.1和43210)。当然,实际使用的位数通常远大于4。
此外,浮点数表示法通常还包括一些特别的数值:+∞和−∞(正负无穷大)以及NaN('Not a Number')。无穷大用于数太大而无法表示的时候,NaN则指示非法操作或者无法定义的结果。
众所周知,计算机中的所有数据都是以二进制表示的,浮点数也不例外。然而浮点数的二进制表示法却不像定点数那么简单了。
先澄清一个概念,浮点数并不一定等于小数,定点数也并不一定就是整数。所谓浮点数就是小数点在逻辑上是不固定的,而定点数只能表示小数点固定的数值,具用浮点数或定点数表示某哪一种数要看用户赋予了这个数的意义是什么。
C++中的浮点数有6种,分别是:
float:单精度,32位
unsigned float:单精度无符号,32位
double:双精度,64位
unsigned double:双精度无符号,64位
long double:高双精度,80位
unsigned long double:高双精度无符号,80位(嚯,应该是C++中最长的内置类型了吧!)
然而不同的编译器对它们的支持也略有不同,据我所知,很多编译器都没有按照IEEE规定的标准80位支持后两种浮点数的,大多数编译器将它们视为double,或许还有极个别的编译器将它们视为128位?!对于128位的long double我也仅是听说过,没有求证,哪位高人知道这一细节烦劳告知。
下面我仅以float(带符号,单精度,32位)类型的浮点数说明C++中的浮点数是如何在内存中表示的。先讲一下基础知识,纯小数的二进制表示。(纯小数就是没有整数部分的小数,讲给小学没好好学的人)
纯小数要想用二进制表示,必须先进行规格化,即化为 1.xxxxx * ( 2 ^ n ) 的形式(“^”代表乘方,2 ^ n表示2的n次方)。对于一个纯小数D,求n的公式如下:
n = 1 + log2(D); // 纯小数求得的n必为负数
再用 D / ( 2 ^ n ) 就可以得到规格化后的小数了。接下来就是十进制到二进制的转化问题,为了更好的理解,先来看一下10进制的纯小数是怎么表示的,假设有纯小数D,它小数点后的每一位数字按顺序形成一个集合:
{k1, k2, k3, ... , kn}
那么D又可以这样表示:
D = k1 / (10 ^ 1 ) + k2 / (10 ^ 2 ) + k3 / (10 ^ 3 ) + ... + kn / (10 ^ n )
推广到二进制中,纯小数的表示法即为:
D = b1 / (2 ^ 1 ) + b2 / (2 ^ 2 ) + b3 / (2 ^ 3 ) + ... + bn / (2 ^ n )
现在问题就是怎样求得b1, b2, b3,……,bn。算法描述起来比较复杂,还是用数字来说话吧。声明一下,1 / ( 2 ^ n )这个数比较特殊,我称之为位阶值。
例如0.456,第1位,0.456小于位阶值0.5故为0;第2位,0.456大于位阶值0.25,该位为1,并将0.45减去0.25得0.206进下一位;第3位,0.206大于位阶值0.125,该位为1,并将0.206减去0.125得0.081进下一位;第4位,0.081大于0.0625,为1,并将0.081减去0.0625得0.0185进下一位;第5位0.0185小于0.03125……
最后把计算得到的足够多的1和0按位顺序组合起来,就得到了一个比较精确的用二进制表示的纯小数了,同时精度问题也就由此产生,许多数都是无法在有限的n内完全精确的表示出来的,我们只能利用更大的n值来更精确的表示这个数,这就是为什么在许多领域,程序员都更喜欢用double而不是float。
float的内存结构,我用一个带位域的结构体描述如下:
struct MYFLOAT
{
bool bSign : 1; // 符号,表示正负,1位
char cExponent : 8; // 指数,8位
unsigned long ulMantissa : 23; // 尾数,23位
};
符号就不用多说了,1表示负,0表示正
指数是以2为底的,范围是 -128 到 127,实际数据中的指数是原始指数加上127得到的,如果超过了127,则从-128开始计,其行为和X86架构的CPU处理加减法的溢出是一样的。比如:127 + 2 = -127;127 - 2 = 127
尾数都省去了第1位的1,所以在还原时要先在第一位加上1。它可能包含整数和纯小数两部分,也可能只包含其中一部分,视数字大小而定。对于带有整数部分的浮点数,其整数的表示法有两种,当整数大于十进制的16777215时使用的是科学计数法,如果小于或等于则直接采用一般的二进制表示法。科学计数法和小数的表示法是一样的。
小数部分则是直接使用科学计数法,但形式不是X * ( 10 ^ n ),而是X * ( 2 ^ n )。拆开来看。
众所周知,计算机中的所有数据都是以二进制表示的,浮点数也不例外。然而浮点数的二进制表示法却不像定点数那么简单了。
先澄清一个概念,浮点数并不一定等于小数,定点数也并不一定就是整数。所谓浮点数就是小数点在逻辑上是不固定的,而定点数只能表示小数点固定的数值,具用浮点数或定点数表示某哪一种数要看用户赋予了这个数的意义是什么。
C++中的浮点数有6种,分别是:
float:单精度,32位
unsigned float:单精度无符号,32位
double:双精度,64位
unsigned double:双精度无符号,64位
long double:高双精度,80位
unsigned long double:高双精度无符号,80位(嚯,应该是C++中最长的内置类型了吧!)
然而不同的编译器对它们的支持也略有不同,据我所知,很多编译器都没有按照IEEE规定的标准80位支持后两种浮点数的,大多数编译器将它们视为double,或许还有极个别的编译器将它们视为128位?!对于128位的long double我也仅是听说过,没有求证,哪位高人知道这一细节烦劳告知。
下面我仅以float(带符号,单精度,32位)类型的浮点数说明C++中的浮点数是如何在内存中表示的。先讲一下基础知识,纯小数的二进制表示。(纯小数就是没有整数部分的小数,讲给小学没好好学的人)
纯小数要想用二进制表示,必须先进行规格化,即化为 1.xxxxx * ( 2 ^ n ) 的形式(“^”代表乘方,2 ^ n表示2的n次方)。对于一个纯小数D,求n的公式如下:
n = 1 + log2(D); // 纯小数求得的n必为负数
再用 D / ( 2 ^ n ) 就可以得到规格化后的小数了。接下来就是十进制到二进制的转化问题,为了更好的理解,先来看一下10进制的纯小数是怎么表示的,假设有纯小数D,它小数点后的每一位数字按顺序形成一个集合:
{k1, k2, k3, ... , kn}
那么D又可以这样表示:
D = k1 / (10 ^ 1 ) + k2 / (10 ^ 2 ) + k3 / (10 ^ 3 ) + ... + kn / (10 ^ n )
推广到二进制中,纯小数的表示法即为:
D = b1 / (2 ^ 1 ) + b2 / (2 ^ 2 ) + b3 / (2 ^ 3 ) + ... + bn / (2 ^ n )
现在问题就是怎样求得b1, b2, b3,……,bn。算法描述起来比较复杂,还是用数字来说话吧。声明一下,1 / ( 2 ^ n )这个数比较特殊,我称之为位阶值。
例如0.456,第1位,0.456小于位阶值0.5故为0;第2位,0.456大于位阶值0.25,该位为1,并将0.45减去0.25得0.206进下一位;第3位,0.206大于位阶值0.125,该位为1,并将0.206减去0.125得0.081进下一位;第4位,0.081大于0.0625,为1,并将0.081减去0.0625得0.0185进下一位;第5位0.0185小于0.03125……
最后把计算得到的足够多的1和0按位顺序组合起来,就得到了一个比较精确的用二进制表示的纯小数了,同时精度问题也就由此产生,许多数都是无法在有限的n内完全精确的表示出来的,我们只能利用更大的n值来更精确的表示这个数,这就是为什么在许多领域,程序员都更喜欢用double而不是float。
float的内存结构,我用一个带位域的结构体描述如下:
struct MYFLOAT
{
bool bSign : 1; // 符号,表示正负,1位
char cExponent : 8; // 指数,8位
unsigned long ulMantissa : 23; // 尾数,23位
};
符号就不用多说了,1表示负,0表示正
指数是以2为底的,范围是 -128 到 127,实际数据中的指数是原始指数加上127得到的,如果超过了127,则从-128开始计,其行为和X86架构的CPU处理加减法的溢出是一样的。比如:127 + 2 = -127;127 - 2 = 127
尾数都省去了第1位的1,所以在还原时要先在第一位加上1。它可能包含整数和纯小数两部分,也可能只包含其中一部分,视数字大小而定。对于带有整数部分的浮点数,其整数的表示法有两种,当整数大于十进制的16777215时使用的是科学计数法,如果小于或等于则直接采用一般的二进制表示法。科学计数法和小数的表示法是一样的。
小数部分则是直接使用科学计数法,但形式不是X * ( 10 ^ n ),而是X * ( 2 ^ n )。拆开来看。
0 00000000 0000000000000000000000
符号位 指数位 尾数位
crisky 2009-05-06
  • 打赏
  • 举报
回复
这个应该BAIDU里有好多吧

16,548

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • AIGC Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧