整數以及浮點數在內存中的存儲

一.整數在內存當中的存儲

數據在內存中是以十六進制補碼的形式進行存儲的。

原碼表示法簡單易懂,適用于乘法,但用原碼表示的數進行加減運算比較復雜,當兩數相加時,如果同號則數值相加,但是進行減法時要先比較絕對值的大小,然后大數減去小數,最后還要給結果選擇恰當的符號。

而負數用補碼表示,加法運算只需要一個加法器就可以實現了,不用再配減法器,可以將符號位和數值域統一處理,此外補碼與原碼相互轉換,其運算過程是相同的,不需要額外的硬件電路。

而1個十六進制可以表示4個二進制位,在內存中查看變量時,只用看(32/4)8位二進制代碼即可

#include<stdio.h>
int main()
{int hh = 15;return 0;
}

再來看一下負數在內存中的情況

#include<stdio.h>
int main()
{int hh = -15;return 0;
}

此時顯示的變量hh在內存中的情況為ff ff ff f1

原碼hh=-15為10000000 00000000 00000000 00001111? 負數最高符號位為1

? ? ? ? ? ?反碼為11111111? ? 11111111? ? ?11111111? ? 11110000

? ? ? ? ? ?補碼為11111111? ? ?11111111? ? ?11111111? ? 11110001

按照一個十六進制位等于4個二進制,將補碼可轉變為 ff? ff ff f1

大小端字序存儲

什么是大小端字序存儲

大小端字序存儲其實就是字節在內存中存儲時的存儲順序。

如果數據的低位字節內容保存在內存的高地址處,而高字節內容保存在內存的低地址處,那么就是大端存儲模式

如果數據的高位字節內容保存在內存的高地址處,而低字節內容保存在低地址處,那么就是小端存儲模式

比如15,它的十六進制補碼為00 00 00 0f

而在vs這個編譯器中存儲的情況是這樣的

編譯器里默認左邊是低地址,右邊是高地址,而存儲為00 00 00 00 0f,其實是按照低字節存低地址的規則來進行的,所以是小端存儲。

代碼判斷大小端存儲

#include<stdio.h>
int check_sys()
{int i = 1;int ret = (*(char*)&i);//先把i的地址轉變為char*,然后解引用會得到內存中存儲的十六進制字節序return ret;//char *只能一個字節一個字節訪問,此時訪問的是第一個內存中的字節
}
int main()
{int ret = check_sys();if (ret == 1)//小端存儲低字節存低位{printf("小端\n");}else{printf("大端\n");}return 0;
}

整型提升

#include<stdio.h>
int main()
{unsigned char a = 200;unsigned char b = 100;unsigned char c = 0;c = a + b;printf("%d %d", a + b, c);return 0;}

為什么這代碼結果會不一樣呢,這就涉及到了整型提升

C語?中整型算術運算總是?少以缺省整型類型的精度來進?的。
為了獲得這個精度,表達式中的字符和短整型操作數在使?之前被轉換為普通整型,這種轉換稱為整型提升。

整型提升的意義

表達式的整型運算要在CPU的相應運算器件內執?,CPU內整型運算器(ALU)的操作數的字節?度?
般就是int的字節?度,同時也是CPU的通?寄存器的?度。?
因此,即使兩個char類型的相加,在CPU執?時實際上也要先轉換為CPU內整型操作數的標準?
度。
通?CPU(general-purpose CPU)是難以直接實現兩個8?特字節直接相加運算(雖然機器指令中可能有這種字節相加指令)。所以,表達式中各種?度可能?于int?度的整型值,都必須先轉換為int或unsigned int,然后才能送?CPU去執?運算。

再回到上面那個代碼

c=a+b

a里面放的是200,200的原碼本來應該是32位的00000000 00000000 00000000 11001000

?正數原反補碼相同都為00000000 00000000 00000000 11001000

放進只有8位的容器char中,發生截斷就為1100 1000

b里面放的是100,100的原碼本來應該是00000000 00000000 00000000 01100100

?正數原反補碼相同都為00000000 00000000 00000000 01100100

放進只有8位的容器char中,發生截斷就為0110 0100

b+c直接相加為100101100,這個得到的是補碼

此時以%d(32位有符號十進制原碼)打印,要先把b+c補碼的結果先填滿為32位,00000000 00000000 00000001?00101100,然后再轉換為原碼,此時最高位為0默認為正數,原反補碼都相同,所以補碼也為00000000 00000000 00000001?00101100,轉換為十進制為300,所以最后打印為300

再來考慮c=a+b,八位a和八位b直接相加為100101100,這是九位,要強行放進只有八位的容器c當中,所以會發生截斷,最高位1會被截斷了。所以c補碼最后為00101100

現在要以%d(32位有符號十進制原碼)打印,要先把c的補碼填滿為32位,00000000 00000000 00000000 00101100,正數轉原碼為000000000 00000000 00000000 00101100,轉換為十進制為44,所以結果為44.

有符號的例子

#include<stdio.h>
int main()
{char a;char b = 1;char c = -1;a = b + c;printf("%d %d", b+c, a);
}

結果是一樣的,但是先別急,試著用上面的方法來分析一下

-1的原碼為10000000 00000000 00000000 00000001

-1的反碼為11111111 11111111 11111111 11111110

-1的補碼為11111111 11111111 11111111 11111111

1的原碼是00000000 00000000 00000000 00000001

這是32位整型的情況,而char類型只能放8位,所以就會發生截斷,char c實際存放的是11111111

1是正數,原反補碼都一樣,都為00000000 00000000 00000000 00000001

這是32位整型的情況,而char類型只能放8位,所以就會發生截斷,char b實際存放的是00000001

b+c直接以十進制原碼的情況打印出來,所以b和c都要重新填滿32位然后相加,這時候要去考慮有符號或者無符號數的情況:無符號數整型提升(填滿32位),高位全補0;有符號數用符號位填滿剩下的位數。

b為有符號數,補碼正數符號位0填滿為00000000 00000000 00000000 00000001

c為有符號負數,補碼符號位1填滿為11111111 11111111 11111111 11111111

補碼直接相加為 100000000 00000000 00000000 00000000,是33位,%d打印要求32位整型打印,所以依舊會截斷,最高位1截斷了,得00000000 00000000 00000000 00000000,此時這得到的是補碼,而此時最高位為0,所以是正數,正數原反補碼相同,最后十進制打印就為0;

再來考慮一下a=b+c打印的情況,如上面說的char b實際存的是8位00000001,char c存的是8位11111111,直接b+c為100000000,九位數要放進只有8位的char a容器里,直接最高位截斷了,即得a=b+c=00000000

?而此時要把a以十進制原碼打印出來,a不足32位,所以要填滿32位,a為有符號數,最高符號位位為0,補滿32位為00000000 00000000 00000000 00000000,最高位為0默認為正數,所以原碼十進制為0,最后打印也為0;

無符號有符號整型提升串用的例子

#include<stdio.h>
int main()
{char a = -1;signed char b = -1;unsigned char c = -1;printf("a=%d,b=%d,c=%d", a, b, c);
}

同樣的分析方式,a,b為有符號數,c為無符號數,打印結果為有符號十進制整型,%u是打印十進制無符號整型

-1的原碼為10000000 00000000 00000000 00000001

-1的反碼為11111111 11111111 11111111 11111110

-1的補碼為11111111 11111111 11111111 11111111

而char a只能放8位,所以實際char a補碼放的是11111111。此時要求輸出十二位十進制原碼,所以a需要整型提升填滿,a是有符號數,此時最高位為1,補碼填滿為11111111 11111111 11111111 11111111,原碼為10000000 00000000 00000000 00000001(符號位不變其余位取反加一),轉換成十進制輸出就是-1。

b同a一樣,也是-1。

而char c補碼實際存放的是11111111,c是無符號數,無符號數整型提升填滿32位是高位全補0,即為00000000 00000000 00000000 11111111,此時最高位為0,要求打印%d類型的數據,會自動把c的最高位認為符號位,正數原反補碼相同,所以原碼為00000000 00000000 00000000 11111111,轉換成十進制打印就是255.

有符號char和無符號char范圍

無符號char最高位為有效位,最高位參與計算,所以范圍為0到255(二進制為1111 1111)

也許有人疑惑,1000 0000反碼不是1111 1111,加1原碼為9位,截斷一位應該為1000 0000啊,最高位為-1,結果不應該為-0嗎

雖然8位二進制中,存在-0(1000 0000)和0(0000 0000),實際生活中0又沒有正負的,-0不也是0嗎,但是它卻有兩種補碼表示,。為了將補碼與數字一一對應起來,就認為把原碼-0的補碼強行人為表示為-128,所以八位二進制-128是沒有反碼和原碼的。看見補碼1000 0000,不用去按照負數求原碼規則取反加一去計算,它直接表示就是-128,這是人為規定的規則

總結有符號char 的范圍為-128到127

如果有符號數最大的正數數127(0111 1111)再加上1,會得到-128(1000 0000),而最大的負數-1(1111 1111)再加1其實得到0000 0000,形成一個環形

同樣無符號數最大的數255加1就得到最小的數0,其實也是個環形

練習

#include<stdio.h>
int main()
{char a = -128;printf("%u\n", a);return 0;
}

32位-128,原碼10000000 00000000 00000000 100000000

反碼為11111111 1111111 1111111 01111111

原碼為11111111 11111111 11111111 10000000

char a是個8位的容器,32位放進去會截斷1000 0000,而接下來要以32無符號整型輸出,8位數要重新填滿32位,即為11111111 11111111 11111111 10000000,要以無符號數十進制輸出,所以最高位1不是符號數,也要參與轉換計算。即為4294967168

二.浮點數在內存中的存儲

IEEE754標準

根據IEEE(國際電氣和電子工程協會)754標準,任意一個浮點數可以寫成下面的形式

十進制的浮點數5.0,寫成二進制為101.0,相當于1.01 x 2^2

按照上面的格式,可得s=0,m=1.01,E=2;

IEEE754規定
對于32位的浮點數,最?的1位存儲符號位S,接著的8位存儲指數E,剩下的23位存儲有效數字M

對于64位的浮點數,最?的1位存儲符號位S,接著的11位存儲指數E,剩下的52位存儲有效數字M

浮點數存的過程


IEEE754對有效數字M和指數E,還有?些特別規定。
前?說過M可以寫成 1.xxxxxx 的形式,其中 xxxxxx 表??數部分。IEEE754規定,在計算機內部保存M時,默認這個數的第?位總是1,因此可以被舍去,只保存后?的xxxxxx部分。?如保存1.01的時候,只保存01,等到讀取的時候,再把第?位的1加上去。這樣做的?的,是節省1位有效數字。以32位浮點數為例,留給M只有23位,將第?位的1舍去以后,等于可以保存24位有效數字。
?于指數E,情況就?較復雜?先,E為?個?符號整數(unsigned int)這意味著,如果E為8位,它的取值范圍為0~255;如果E為11位,它的取值范圍為0~2047。但是,我們知道,科學計數法中的E是可以出現負數的,所以IEEE754規定,存?內存時E的真實值必須再加上?個中間數,對于8位的E,這個中間數是127;對于11位的E,這個中間數是1023。?如,2^10的E是 10,所以保存成32位浮點數時,必須保存成10+127=137,即10001001。

舉個存儲的例子

0.5存儲,二進制為0.1,規定正數部分必須為1,所以右移操作得1.0*2^(-1),這是正數所以s=0,

E的存儲要加127(1023),-1+127=126,二進制表示為0111 1110.

此時M為1.0,按照規則要舍去1,只保留0,而M要存23位,bu'qi為

浮點數取的過程
?

浮點數取的過的過程可以分為三種情況

E的表示不全為0或者不全為1,這也是最常見的情況

這是浮點數取出E的規則是E的計算值減去127(或者1023),就是把前面存的時候加上的中間數減去,還原原數

E全為0的情況

這時,浮點數指數E等于1-127(或者1-1023)即為真實值,有效數字M不再加上第一位的1,而是還原為0.xxxxxx的小數。這樣是為了表示+-0,以及接近0的很小的數字

E全為1的情況

這時,如果有效數字M全為0,表示+-無窮大(正負取決于符號位s)

相關浮點數存儲的例題

#include <stdio.h>
int main()
{int n = 5;float* p= (float*)&n;printf("n的值為:%d\n", n);printf("p的值為:%f\n", *p);*p = 5.0;printf("num的值為;%d\n", n);printf("*p的值為;%f\n", *p);
}

為什么結果會這么奇怪

首先整數5的二進制型式為 00000000 00000000 00000000 00000101

這時候取地址然后強制轉換為float *類型,并用浮點數類型指針p去保存它,此時解引用實際上是把上面那一串二進制數以浮點數的形式取出來

而浮點數形式要考慮占8位的E和占23位的M的值

實質就是以下圖V的形式進行輸出的

5的32位整數二進制形式 00000000 00000000 00000000 00000101

s是二進制序列中的第一位,所以s=0;

E為00000000 ;

M所屬的剩下的23位為00000000 00000000 0000101

還原成V的形式,E在32位電腦下要先減去127,所以指數E為-127

而此時E全為0,所以M的1可以不用加

v=(-1)^0 x 0.00000000 00000000 0000101 x 2^(-127)=(-1)^0 x? 1.01 x2^(-148)

此時V是非常接近0的數,極限逼近0,所以輸出為0.000000

然后第二環節*p=5.0,是直接把n改寫成浮點數的形式,此時要以整數的形式把它輸出

浮點數5.0的二進制形式為0101.0,換算成科學計數法形式為1.01 x 2^(2)

按上圖理解 s=0,M為1.01,E為2

E占8位,且要先+127,所以E表示為100000001

M的1要省略,有效數字為01,要補滿23位,所以M為 01000000 00000000 0000000

寫成二進制形式S+E+M形式

0 10000001 01000000 00000000 0000000

轉換為十進制為1084227584

總的來說,如果一開始n為整數,而現在要用浮點數形式打印出來,實際是浮點數取的規則

如果一開始就為浮點數,那么以32位整數的形式打印出來,實際使用的是浮點數存的規則

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/208558.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/208558.shtml
英文地址,請注明出處:http://en.pswp.cn/news/208558.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

認知覺醒(六)

認知覺醒(六) 第二節 感性&#xff1a;頂級的成長竟然是“憑感覺” 人類生存于世&#xff0c;比拼的是腦力思維&#xff0c;但極少有人知道&#xff0c;我們的身體里還有一個更高級的系統&#xff0c;若能善用&#xff0c;成就非凡。 1941年&#xff0c;德軍對英國本土進行…

Neo4j介紹

1、Neo4j介紹 Neo4j 是一個圖數據庫管理系統&#xff0c;它專注于存儲和處理圖形結構的數據。圖數據庫是一類特殊的數據庫&#xff0c;用于有效地管理圖形數據模型&#xff0c;其中數據以節點、關系和屬性的形式存儲。 2、Neo4j特點 圖數據庫&#xff1a; Neo4j 是一種 NoSQ…

目標檢測器技術演進簡史

引言 目標檢測算法的發展已經取得了長足的進步&#xff0c;從早期的計算機視覺方法開始&#xff0c;通過深度學習達到了很高的準確度。在這篇博文中&#xff0c;我們將一起回顧一下這些算法的發展階段以及現代目標檢測系統中使用的主要方法。 我們首先回顧早期傳統的目標檢測…

大數據技術3:數據倉庫的ETL和分層模型

前言&#xff1a;我們先了解一下數據倉庫架構的演變過程。 1 、數據倉庫定義 數據倉庫是一個面向主題的&#xff08;Subject Oriented&#xff09;、集成的&#xff08;Integrate&#xff09;、相對穩定的&#xff08;Non-Volatile&#xff09;、反映歷史變化&#xff08;Time…

電商系統架構演進

聊聊電商系統架構演進 具體以電子商務網站為例&#xff0c; 展示web應用的架構演變過程。 1.0時代 這個時候是一個web項目里包含了所有的模塊&#xff0c;一個數據庫里包含了所需要的所有表&#xff0c;這時候網站訪問量增加時&#xff0c;首先遇到瓶頸的是應用服務器連接數&a…

深入體驗:山海鯨可視化軟件的獨特魅力

山海鯨可視化軟件是一款功能強大的數據可視化工具&#xff0c;作為該軟件的資深用戶&#xff0c;我深感其獨特的魅力和優勢。下面&#xff0c;我將從軟件特點、操作體驗、數據交互和實際應用場景等方面&#xff0c;為大家詳細介紹山海鯨可視化軟件。 首先&#xff0c;山海鯨可視…

解決Eslint和Prettier關于三元運算符的沖突問題

三元運算符Prettier的格式化 三元運算符Eslint的格式要求 解決辦法 // eslint加入配置&#xff0c;屏蔽標紅報錯indent: [error, 2, { ignoredNodes: [ConditionalExpression] }]效果

Nginx按指定格式記錄訪問日志

今天突然想起來一個日志的一個東西,因為拉項目無意中看到了日志文件的一些東西,現在不經常做后端了,加上其他的一些原因吧.有時候有些問題也沒想太多,馬馬虎虎就過了,后來想想還是要記錄一下這方面的處理過程吧: 一般我們作為開發人員關注的日志只是在應用程序層面的,我們稱它…

LSTM_預測價格問題_keras_代碼實操

0、問題描述 使用Bicton數據集&#xff0c;對close數據進行預測&#xff0c;使用60個數據點預測第61個數據點。 下載數據集&#xff1a;Bitcoin Historical Data 前期已經使用了MLP和RNN進行預測&#xff1a;這里 1、 沒有寫完&#xff0c;明天再寫&#xff1a;&#xff09;…

POJ 3735 Training little cats 動態規劃(矩陣的冪)

一、題目大意 我們有N只貓&#xff0c;每次循環進行K次操作&#xff08;N<100&#xff0c;K<100&#xff09;&#xff0c;每次操作可有以下三種選擇&#xff1a; 1、g i 給第i只貓1個食物 2、e i 讓第i只貓吃完它所有的食物 3、s i j 交換第i和j只貓的食物。 求出M次…

JS自己定義數組擴展方法 求和 和 最大值、最小值

相信有小伙伴看到這一個標題可能會想&#xff1a;現在都可以自己寫方法了嗎&#xff1f;這么炸裂。沒錯我們是可以自己寫方法的。 1.我們定義的這個方法&#xff0c;任何一個數組實例對象都可以使用 2.自定義的方法寫到 數組.propertype身上 最大值 const arr [1,2,3,4]Array…

銷售技巧培訓之如何提高手機銷售技巧

銷售技巧培訓之如何提高手機銷售技巧 隨著科技的迅速發展&#xff0c;手機已成為我們日常生活中不可或缺的一部分。作為一名手機銷售員&#xff0c;了解手機銷售技巧是必不可少的。本文將通過案例分析與實踐&#xff0c;為你揭示手機銷售的奧秘。 一、了解客戶需求 在銷售過程…

AWS Remote Control ( Wi-Fi ) on i.MX RT1060 EVK - 3 “編譯 NXP i.MX RT1060”( 完 )

此章節敘述如何修改、建構 i.MX RT1060 的 Sample Code“aws_remote_control_wifi_nxp” 1. 點擊“Import SDK example(s)” 2. 選擇“MIMXRT1062xxxxA”>“evkmimxrt1060”&#xff0c;并確認 SDK 版本后&#xff0c;點擊“Next>” 3. 選擇“aws_examples”>“aw…

在 Docker 容器中運行 macOS:接近本機性能,實現高效運行 | 開源日報 No.96

cxli233/FriendsDontLetFriends Stars: 2.6k License: MIT 這個項目是關于數據可視化中好的和不好的實踐&#xff0c;作者通過一系列例子解釋了哪些圖表類型是不合適的&#xff0c;并提供了如何改進或替代它們。主要功能包括展示錯誤做法以及正確做法&#xff0c;并提供相應代…

【數值計算方法(黃明游)】解線性代數方程組的迭代法(一):向量、矩陣范數與譜半徑【理論到程序】

文章目錄 一、向量、矩陣范數與譜半徑1、向量范數a. 定義及性質補充解釋范數差 b. 常見的向量范數 l 1 l_1 l1?、 l 2 l_2 l2?、 l ∞ l_\infty l∞? 范數性質關系 2、矩陣范數a. 矩陣的范數b. 常見的矩陣范數相容范數算子范數 3、譜半徑4、知識點總結1. 向量范數2. 矩陣范數…

Mybatis XML 多表查詢

這篇需結合 <<Mybatis XML 配置文件>>那一篇博客一起看 工作中盡量避免使用多表查詢,尤其是對性能要求非常高的項目 我們之前建了個用戶表(代碼在Mybatis XML配置文件那篇博客里),這次再建一個文章表,代碼如下 : -- 創建?章表 DROP TABLE IF EXISTS articleinf…

vue中組件傳值方法

父組件給子組件傳值 一、 1.在子組件標簽中寫入父組件傳遞數據 向下傳遞prop 2.在子組件內聲明props選項接收父組件傳遞的數據 props:[,,] 父組件&#xff1a; <Header :msgmsg ></Header> 子組件&#xff1a; props:[msg], 二、 provide i…

vue 批量下載文件,不走后端接口的方法

今天ld提了一個需求&#xff0c;說頁面的列表里面有要下載的地址,然后點擊批量下載。我思索片刻&#xff0c;給出了代碼 1.這個是列表頁面的代碼 <!-- 這個是列表頁面的代碼 --> <el-table :data"userListShow" align"center"border highlight-…

AI 訓練框架:Pytorch TensorFLow MXNet Caffe ONNX PaddlePaddle

https://medium.com/jit-team/bridge-tools-for-machine-learning-frameworks-3eb68d6c6558

基于jsonrpc4j實現JSON-RPC over HTTP(服務端集成Spring Boot)

1.JSON-RPC說明 JSON-RPC是一個無狀態且輕量級的遠程過程調用(RPC)協議。 它主要定義了一些數據結構及其相關的處理規則。 它運行時可以基于tcp(socket),http等不同的消息傳輸方式&#xff0c; 即它不關心底層傳輸方式的細節。 它使用JSON&#xff08;RFC 4627&#xff09;作為…