一篇文章带你彻底弄清C语言中的整形与浮点型究竟是如何存储

C语言中有许多数据类型,使用类型的不同决定了所开辟的空间大小(大小决定了使用范围)及如何看待内存空间的视角,那么今天我们就来一起从0开始学习C语言中整形与浮点型的存储方式,(看完从小白秒变高手!!!):

目录

整形数据的存储(int、long等)

这里关于补码额外说几点(加深理解部分,可跳过,不影响后续阅读)

浮点型的存储方式(float  double等)

整形数据的存储(int、long等)

首先我们先来明确以下几个概念:原码、反码、补码

计算机中的有符号数(整形)有三种表示方式:原码、反码、补码

(三种表示方式均由符号位与数值位两部分构成,符号位(首位)用0表示正数,符号位用1表示负数)

原码:将数字直接按照二进制的翻译方法翻译过来即可

反码:原码的符号位不变,其他位按位取反即可

补码:给原码+1即为补码

·对于无符号数(可以理解为正整数)来说原码、反码、补码都相同

而计算机存储的都是整形数据的补码

  接下来,我们以整形a为例:

int a = -10;

那么a的原码为

10000000 00000000 00000000 00001010

11111111 11111111 11111111 11110101 -- 反码

11111111 11111111 11111111 11110110 -- 补码

接下来转化为十六进制则为

0xFFFFFFF6 

则a在内存中的存储方式为:

F6 FF FF FF

(一般为小端存储,大小端存储部分内容过两天写完放主页)

这里关于补码额外说几点(加深理解部分,可跳过,不影响后续阅读)

为什么要用这种看似复杂的转化来表示负数呢?

其实是与计算机的组成有关,计算机的CPU只能运算加法,那么当遇到减法的时候怎么办呢,于是就有人提出了补码的概念,将一个负数求出补码后,直接相加,这样就巧妙规避了减法的情况。

但我们完全可以对补码做更深一步的理解以提高我们的基本功:

#include<stdio.h>
int main()
{
    char a[100];//创建一个有100个元素的char类型数组
    int i = 0;
    for(i = 0; i< 1000; i++)//i从1自增到999
    {
        a[i] = -1 - i;//给每个-i都减-1并存入数组中
    }
    return o;
}

 该程序有趣的点在于数组a中究竟有几个元素,元素分别是什么?

char类型占据一个字节,即8个比特位,那么就意味着存储范围只能是-128到127

for循环内部可以看作a[i]从-1逐渐自减的过程,那么,a[i]在自减至-128时,再自减变为-129,-129却又超出了char类型的存储范围,显然不成立,我们接下来来讨论-128 - 1的这个过程:

10000000000000000000000010000000 -- 负128的原码

11111111111111111111111101111111 -- 负128的反码

11111111111111111111111110000000 -- 负128的补码

给补码加上-1

11111111111111111111111111111111 -- 负一的补码

与负128的补码直接相加得:

11111111111111111111111101111111 -- 得到了-129的补码

在进行后8位截断(关于整形运算中的整型提升与截断过两天我写出来放主页)

01111111

此时我们发现符号位为0,也就是说该数此时被认为成正数,正数原码、反码、补码都相同,所以直接翻译为127,再进行自减,自减至0时(‘/0‘的ASCII码为0),字符串结束,程序终止。

此时程序虽然已经终止,但我们不妨设想以下如果它继续运行,将会发生什么,0 - 1 = -1,接下来-1继续进行自减,也就是说自减的过程相当于一个首尾相接的圈子,又0开始减一,减到-128时再减会变为127,再逐步减回0,同样相加也会逐步加回0。

int型的存储也会是像这样一样“反反复复”,有兴趣的朋友可以自己证明,本文不再做过多赘述。

看到这里不知道你对补码的认知是否又深刻了一步呢?

浮点型的存储方式(float  double等)

浮点型在计算机中的存储方式如下:

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

S用于表示该数的正负(0或1)

M用于表示该数的二进制科学计数法有效数字(1<=M<2)

E用于表示幂次

(该表示方法其实等价于十进制中的科学计数法,M是有效位,2^E代表在二进制中小数点的移动操作等效于十进制数中的10^N。)

例如数字9:

我们首先将9写成上述标准形式

9  = (-1)^0 * 1.001*2^3

接下来我们对32个bit位以1、8、23区间长度进行划分(float为4个字节,32个bit位)

0 00000000 00000000000000000000000

·第一个比特位用来存放s(表示数字的正负,相当于符号位)

·第2到9个比特位用来存放E

     (在存放E的时候我们需要注意,因为次数项有可能为负数,但是这八个比特位规定是以无符号数的形式存储,所以次方项我们通常采取将给原数+127的方式得到的值存储,翻译时再减去127)

·其余的23位存储有效值(小数部分用0向后补位)

     (关于有效位的存储,因为M的值一定是大于等于1小于2的,所以我们不去存储有效数字小数点前的那一位1,只取小数点后的数字存储)

则9以浮点数的形式存储的真实情况为

0 10000010 00100000000000000000000

转化为十六进制即为 41 10 00 00

再按照小端存储的方式放入内存中:00 00 10 41

这里关于E有两种特殊情况:

1、若存放E的8个bit位全为0,则我们认为E的真实值为1 - 127而不是-127,并且在翻译时不再加上有效位中的1,而是表示为0.xxxxxxx,此法则是为了表示0或非常小的数。

2、若存放E的八位全为1,且存放M的23位均为0,此法则是为了表示正负无穷大的数。

注:以上情况默认为float类型32位,若是64位则E的位数变为11位,其余逻辑相同,这里不再做过多说明。 

---------------------------------------------------------------------------------------------------------------------------------

有不同理解的朋友欢迎在评论区留言,不足的地方也希望大家帮忙纠正,本系列将会持续更新,刚入门的朋友们可以跟随文章一步步变强,码字不易,喜欢本文的话请记得点赞,祝我们都有美好的未来!!!!!!!!!!!!!!

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
递归算法是一种重要的计算机科学概念,在解决问题时可以极大地提高效率和简化实现。硅谷工程师可以用十五分钟为我们深入理解递归算法。 首先,递归算法是一种通过将一个问题分解为多个子问题解决的方法。这些子问题具有相同的结构,只是规模更小。递归算法通过不断递归调用自身来解决这些子问题,直到达到基本情况,即最小规模的问题能够直接解决。 其次,理解递归算法的关键是弄清如何将一个大问题分解为多个小问题。这可以通过定义递归函数来实现。递归函数是一个可以调用自身的函数,它接受某个问题的输入,并将其划分为更小的子问题。在处理子问题时,递归函数会再次调用自身,直到达到基本情况。 递归算法的一个重要概念是递归调用栈。当一个函数被调用时,计算机会将当前函数的状态保存在栈。每次递归调用时,计算机会为当前函数创建一个新的栈帧,并将其添加到栈。当基本情况被达到时,递归函数开始返回结果,并逐个退出栈帧,直到回到最初的调用点。 递归算法的优点是能够以简洁的方式解决复杂的问题,但需要注意的是,如果递归没有适当的停止条件或者递归过深,可能会导致栈溢出等问题。因此,在实现递归算法时,需要合理设置停止条件,并设计好递归终止的条件。 总结来说,递归算法是通过将一个问题分解为多个子问题来解决的方法。实现递归算法的关键是定义递归函数,将问题划分为更小的子问题,并通过递归调用来解决它们。理解递归算法的核心概念包括递归调用栈和递归终止条件。通过正确理解和运用递归算法,可以更高效地解决问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值