浮点数在计算机中的表示方法

以下转载自:https://www.cnblogs.com/silentNight/p/5274714.html

一个哥们在qq群里问了一个关于浮点数的程序,然后行了行浮点数的知识.竟然忘了,所有找了些文章.回忆回忆,理解理解

首先来聊天他的问题和让我无言以对的解决办法吧

"“十六进制转负数浮点数怎么转换啊”

然后我默默的写了一个下面的东东

#include "stdafx.h"
#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
using namespace std;
 
float getFloat()
{
    return (float)3.1423;
}
 
string FloatToHex ( float fNum )
{
    int nInteger = (int)(fNum);
    int nPower = 0;
    while( fNum - nInteger > 1e-5 )
    {
        fNum *= 10;
        nInteger = (int)(fNum);
        nPower++;
    }
    std::cout<<nPower<<std::endl;
 
    return "";
}


string FloatLength ( float fNum )
{
    char cAryFloat[100] = {0};<br>     sprint(cAryFloat, "%g", fNum);//从字符串cAryFloat中统计小数点后面有几位!!!  %f 3.142300  %g 3.1423  %e 31423 + 1e-4
    
    return "";
}

int _tmain(int argc, _TCHAR* argv[]) { FloatToHex(getFloat()); getchar(); return 0; } /* 在getFloat()函数中如果 return (float)3.14 那么最后打印的是2 在getFloat()函数中如果 return (float)3.1423 那么最后打印的是7 纠结了半天,猜测着可能你是返回3.1423但是你没办法控制float的精度,所有最后会打印7吧.

在这里插入图片描述

float最后可能是31422.9999999 打印的时候打印出了31423.但是强转int的话int仍然是32422.<br>所以又写了另外的函数string FloatLength ( float fNum ) 这个统计起来就没问题了..

下面的是他的代码:

int i = 0xbd600d1b;
float *f = (float *)&i;
printf("%f\n",*f);
float ff = -0.0547;
int *ii = (int *)&ff;
printf("%X\n",*ii);

反正也对吧…但是总有一种无言以对的感觉…

接下来就要进入正题,讲一讲浮点数在内存中的表示了.

首先是在c和c#中的浮点类型分两种 单精度float和双精度double. float或者double在内存中的组织都遵循IEEE的规范的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53

然后呢对于一个存储在内存的浮点数都包括3部分 : 符号位 指数位 小数部分 (而float和double的区别就是各部分占得位数可能不同)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

恩.以上基本就可以把float和double的东西搞得差不多了…

大牛还弄了个例子来说明把一个float赋值给double的话 double的值未必等于float…我们来看看吧

猜测一下下面的程序的输出结果是什么:

float f = 2.2f;
double d = (double)f;
Console.WriteLine(d.ToString("0.0000000000000"));
f = 2.25f;
d = (double)f;
Console.WriteLine(d.ToString("0.0000000000000"));    

可能输出的结果让大家疑惑不解,单精度的2.2转换为双精度后,精确到小数点后13位后变为了2.2000000476837,而单精度的2.25转换为双精度后,变为了2.2500000000000,为何2.2在转换后的数值更改了而2.25却没有更改呢?很奇怪吧?

首先我们看看2.25的单精度存储方式,很简单 0 1000 0001 001 0000 0000 0000 0000 0000,而2.25的双精度表示为:0 100 0000 0001 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000,这样2.25在进行强制转换的时候,数值是不会变的,
在这里插入图片描述

ps: 先插入一个怎么把十进制小数转换成二进制的小数的方法---->

在这里插入图片描述

在这里插入图片描述

但是这样存储方式,换算成十进制的值,却不会是2.2的,应为十进制在转换为二进制的时候可能会不准确,如2.2,而double类型的数据也存在同样的问题,所以在浮点数表示中会产生些许的误差,在单精度转换为双精度的时候,也会存在误差的问题,对于能够用二进制表示的十进制数据,如2.25,这个误差就会不存在,所以会出现上面比较奇怪的输出结果。

恩,学习完了.附上原作者的链接 http://www.cnblogs.com/jillzhang/archive/2007/06/24/793901.html
https://blog.csdn.net/nesll/article/details/52302520
https://blog.csdn.net/j864458340/article/details/78435230
https://blog.csdn.net/ganxingming/article/details/1449526


以下转载自:
https://blog.csdn.net/gunqu_d/article/details/76401283

整型与浮点型数据在计算机内存中的存储

1、类型的归类

整型家族:char、unsigned char、signed char //对于char来说,标准里并不默认其为有符号还是无符号的,这个结果取决于编译器。在计算机中,我们实际上可以用整形数据来模拟定点数运算。

                    short ( signed short [int] )、unsigned short [int]

                    int ( signed int )、unsigned int

                    long ( signed long [int] )、unsigned long [int]

浮点数家族

  float

double

构造类型:数组类型

                    结构体类型 struct

                    枚举类型 enum

                    联合类型 union

指针类型

空类型

2、整型在内存中的存储

1)整型在内存中以补码的形式存储,浮点数则没有补码之说,它只需要规定指数与尾数。

原因有三:其一,使用补码可以将符号位和数值域统一处理;其二,加法和减法可以统一处理(cpu只有加法器);其三,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

2)数据存储的大小端模式

小端字节序存储:数据的低字节存到低地址处,高字节存到高地址处。

大端字节序存储:数据的低字节存到高地址处,高字节存到低地址处。

存在不同字节序存储模式的原因:因为在计算机系统中是以字节做单位的,每个地址单元对应一个字节,一个字节有8个比特位。不过在c语言中,除了具有一个字节(8b)的char类型外,还有2个字节(16b)的short型、4个字节(32b)的int型等,而且对于位数大于8的处理器,由于寄存器宽度大于一个字节,所以就必然存在如何安排多个字节的问题。这就是小端存储模式与大端存储模式出现的原因。

例如对于一个short类型的x数据,在内存中的地址为0x0010,其值为0x1122,那么0x11是x的高字节,0x22是x的低字节。对于大端存储模式,0x11放在低地址的0x0010中,0x22放在高地址的0x0011中。对于小端存储模式则相反。

关于大端小端字节序:
https://blog.csdn.net/m0_37949304/article/details/75576631

3、浮点数在内存中的存储

1)根据国际标准IEEE754,任意一个二进制浮点数V可以表示为下面的形式:

(-1)^S*M*2^E

(-1)^S表示符号位,当S=0时,V为正数;当S=1时,V为负数

M表示有效数字,大于等于1,小于2

2^E表示指数位

2)对于32位的浮点数和64位浮点数的不同规定

对于32位的浮点数,最高1位是符号位S,接着是8位的指数E,剩下的23位是有效数字M。

对于64位的浮点数,最高1位是符号位S,接着的11位是指数E,剩下的52位是有效数字M。

3)几点注意

第一,对于有效数字M,因为其值大于等于1而小于2,所以在计算机中,为了能够利用23位(52位)表示更多的数据,IEEE754规定保存M时默认这个数的第一位为1,所以只保存后面的部分(小数点后的位)。然后等到读取此数的时候,再把第一位的1加上去。

第二,对于指数E,为了能够表示负数的指数,IEEE754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数为127;对于11位的E,这个中间数是1023。

第三,当E全为0时,读取该数字时有效数字M不再加上第一位的1,因为这是一个无限接近与0的数字,表示正负0;当E全为1的时候,若M全为0,则表示一个正负无穷大的数。


以下转载自:
https://blog.csdn.net/xiaofeng119/article/details/6176446


再看个论坛的例子吧,其实这个问题比较傻,浮点数根部不谈补码,而小数却有定点数表示,只有整形数才有补码,定点数说补码的概念都过于勉强:

小数的补码怎么求?正负小数的补码怎样求?和整数的规律一样吗,求举例

https://bbs.csdn.net/topics/391841677?page=1

看下网友回答吧。。

网友1:
没见过,这种实现,定点小数?

网友2:
浮点数在内存中是用符号位、指数位、尾数部分组成的,和整数在内存中的模型不同的,具体参看:http://www.cnblogs.com/xkfz007/archive/2012/02/27/2370357.html

网友3:
浮点数,不采用补码表示
整数中,有符号数,用的是补码数表示
如果采用定点数,表示小数,那就和整数相当了。

网友4:
类似 —1010101.1010101
小数部分怎么求补码

网友5:
定点数 小数其实就是整数
只是输入输出按照小数输入输出而已
必要时,可能需要四舍五入处理

整型数据和字符串数据在内存中的存放

原来对于数据在内存中的存放有点模糊,昨天翻出来大学时候的c语言课本,仔细研究了一下。数据在计算机中都会以二进制的形式进行存放,其中对于整型数据在内存中的存放都是以补码的形式存放的,正数的补码是本身,负数的补码是其绝对值加1.而对于字符型数据会把字符所对应的ASCII码存入到内存中,下面以int和char类型的为例:

整型数据在内存中的存放形式

如果定义了一个整型变量i:

int i;

i=10; 

在这里插入图片描述
数值是以补码表示的:

n 正数的补码和原码相同;

n 负数的补码:将该数的绝对值的二进制形式按位取反再加1。

例如:

求-10的补码:

10的原码:
在这里插入图片描述

由此可知,左面的第一位是表示符号的。

整型数据在内存中所占的位数(如下图所示)

在这里插入图片描述
字符数据在内存中的存放形式

如果定义了一个字符串变量i:

char a;

a = ‘x’

在这里插入图片描述

x的ASCII码为120,转换成二进制为1111000
在c语言中字符数据占一个字节8位


最后附上一个例子吧:

输出数字在内存中呈现的二进制数值

以下例子用于输出一个数字在内存中呈现的二进制数值。
其中有一些知识点需要注意:

1.sizeof(num)用于获得num在内存中占用的字节数
2.<< 是二进制位左移操作

     num<<1 --相当于-- num*2
     num<<2 --相当于-- num*2*2
     num<<n --相当于-- num*2^n(2的n次方)

3.>> 是二进制位右移操作

 num>>1 --相当于-- num/2
 num>>2 --相当于-- num/(2*2)
 num>>n --相当于-- num/2^n(2的n次方)

4.& 是二进制位与操作,任何数与1位与都可以得到这个数二进制的最低位

8 & 1 --相当于-- 0b1000 & 0b0001

    1000
    0001
   ------
    0000

因此计算结果为 0 --相当于-- 0b0000(相较于原文有改动)

以下为例子:

void printBinary(int num);

int main()
{
    int q;
    do
    {
        int num;
        printf("输入一个数\n");
        scanf("%d",&num);
        printBinary(num);
        printf("按任意键继续,0退出!\n");
        scanf("%d",&q);
    }
    while(q);
}

//打印数值对应的二进制
void printBinary(int num)
{    
    // sizeof(num)<<3 --相当于-- sizeof(num)*8
    int cnt=(sizeof(num)<<3)-1;
    for(int i=cnt;i>=0;i--)
    {
        // num>>i --相当于-- num/2^i(2的i次方)
        //此例中的位移只是为了进行位与运算(&)得到最低位的值
        int temp=num>>i;
        int b=temp&1;
        printf("%d",b);

        if(i%4==0)
        {
            printf("  ");
        }
    }
    printf("\n");
}

输出结果

输入一个数

1
0000  0000  0000  0000  0000  0000  0000  0001  
按任意键继续,0退出!
1
输入一个数
10
0000  0000  0000  0000  0000  0000  0000  1010  
按任意键继续,0退出!
0

其实关于上面这个问题,还有一种算法思路,可能会更加高效。

参考自:https://blog.csdn.net/linfeng24/article/details/34209271

今天在华为OJ上遇到这么一个题目,很简单,但是却总是得不到最好的成绩记录。因此比较了自己的程序、思路与别人的异同,发现还是有很大区别的,遂记录如下。

题目——

输入一个int型整数,求该整数的二进制在内存中有多少个1。例如输入10,由于其二进制表示为1010,有两个1,因此输出2。

思路1

public static void main(String[] args)      {  
    	 
    
         Scanner scanner = new Scanner(System.in);
        int m = scanner.nextInt(); 
        int num = 0;
        while(m>0){
        	m&=(m-1);
        	num++;
        }
        
   
     } 

这是最常规的思路。直接利用移位去算,始终判断最后一位是不是1,然后计数。这也是我一开始所所想到的,但是,这种写法只得到了60分。

思路2

private static int count_ones(int a)     {
    	    a = (a & 0x55555555) + ((a >> 1) & 0x55555555);
    	    a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
    	    a = (a & 0x0f0f0f0f) + ((a >> 4) & 0x0f0f0f0f);
    	    a = (a & 0x00ff00ff) + ((a >> 8) & 0x00ff00ff);
    	    a = (a & 0x0000ffff) + ((a >> 16) & 0x0000ffff);
 
    	    return a;
    	}

这是我看别人拿了满分的答案。这种思路比较难想到。一开始我怎么都想不明白他是怎么想出来的。直到翻看书籍,碰巧在《编程之美》上有记载这个题目,并且给出了五种解法。其中有两种就是以上两种。

这道题的本质相当于求二进制数的 Hamming 权重,或者说是该二进制数与 0 的 Hamming 距离,这两个概念在信息论和编码理论中是相当有名的。在二进制的情况下,它们也经常被叫做 population count 或者 popcount 问题,比如 gcc 中就提供了一个内建函数:int __builtin_popcount (unsigned int x) 输出整型数二进制中 1 的个数。但是 GCC 的 __builtin_popcount 的实现主要是基于查表法做的,跟编程之美中解法 5 是一样的。Wikipedia 上的解法是基于分治法来做的,构造非常巧妙,通过有限次简单地算术运算就能求得结果,特别适合那些受存储空间限制的算法中使用。

此外,在这个博客里也有对编程之美里五种解法的详细分析,值得一读。http://blog.csdn.net/shijiemazhenda/article/details/6785674


再看两个例子:

数字字符转换成数字

输入两个数字字符,将它们转换成十进制后输出

#include <stdio.h>
int main()
{

    char ch;
    int i,data;
    data=0;
    for(i=0;i<2;i++)
    {
        while(1)
        {
            ch=getchar();
            if(ch>='0'&&ch<='9')
            {
                break;
            }
        }
        data=data*10+ch-'0';
    }
    printf("data=%d\n",data);
}

十进制转换
data=data*10+ch-‘0’;

删除一个字符串中的所有数字字符

#include <stdio.h>
void delnum(char *s)
{
  int i,j;
  for(i=0,j=0;s[i]!='\0'  ;i++)//1
  if(s[i]<'0'||s[i]>'9')//2为什么?
  {
    s[j]=s[i];//3
    j++;
  }
  s[j]='\0';
}
int main ()
{
  char item[1001];
  printf("input a string:\n");
  gets(item);
  delnum(item);//4
  printf("\n%s",item);
}
  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值