聊一聊定點數和浮點數的存儲方式


點擊藍字 關注咱們node



定點數和浮點數

本文中所提到的都是基於intel x86Cpu,開發環境是基於windows 10 + vs2019。本片博客須要讀者本身明白十進制、十六進制、二進制之間的轉換,文中不會介紹轉換過程,須要瞭解詳細過程的情查找相關資料web

首先咱們簡單的介紹一下計算機中數據存儲的基礎知識,具體內容在大學課程《計算機組成原理》中有詳細的介紹。計算機中對數據的存儲是有兩種形式,一種是以定點數方式存儲即C/C++中的char、short、int、long、longlong,另外一種就是以浮點數的方式存儲即C/C++中的float、double編程

定點數中char、short、int、long、longlong存儲原理都是同樣的,只是長度不一樣,因此咱們選取int型詳細介紹,浮點數中float、double原理也是同樣的.windows

其中,定點數和浮點數都是最高位表示符號位(0表示正數1表示負數)其他位表示數值,字節是倒敘存數(小端模式)的也就是說高字節在左邊低字節在右邊。微信

定點數

定點數比較簡單,計算機中存儲的是真實值,計算機採用4字節(32位)存儲int變量,例如:int value = 1 ; 則value轉化成二進制0000 0000 0000 0000 0000 0000 0000 0001爲了書寫方便咱們寫成16進制形式爲00 00 00 01。因爲計算機是倒敘存儲因此計算機中存儲爲01 00 00 00。app

浮點數

下面介紹一下浮點數float存儲,C/C++中float存儲標準是基於IEEE754,具體內容是一個數值,可使用科學計數法方式表示,便可以寫成:編輯器

A * 2^n佈局

其中A爲尾數,2爲底數,n爲指數。學習

因爲底數爲2因此全部A都是大於1小於2,也就是均可以寫成1.xxxx*2的n次方,因此計算機爲了節省空間,能夠不存儲整數部分的1,由於全部的浮點數都有那個1咱們轉換的時候把那個1算進去就能夠了。n能夠是正數也能夠是負數(float須要加上127,double加上1023)。flex

浮點數的存儲是由符號位 (sign) + 指數位 (exponent) + 小數位 (fraction) 組成。

類型 符號位 指數 尾數
Float      1位(第31位) 8位(第23~30位) 23位(第0~22位)
Double   1位(第63位) 11位(第52~62位) 52位(第0~51位)

例如:float value = 12.5 ; 那麼

定點數 浮點數

小數轉二進制方法:整數部分採用除 2 取餘,小數部分採用乘 2 取整法

float計算機存儲(小端模式)即00 00 48 41

double計算機存儲(小端模式)即  00 00 00 00 00 00 29 40

查看內存驗證結果

#include<stdio.h>
int main()
{
float a = 12.5;
float* p = &a;

double b = 12.5;
double* pb = &b;

printf("%d\n", a);
printf("%d\n", (int)a);
printf("%d\n", *(int*)&a);
return 0;
}

經過斷點查看變量a,b的地址,經過地址用內存查看器查看對應的內存來驗證

VS2019運行至斷點時,菜單調試->窗口->內存->內存1

double

內存是不會騙人的,因此結果驗證無誤

大端小端

大端模式指數據的高字節保存在內存的高地址

例如:12345( 0x3039 ) 的存儲順序是 0x30、0x39

小端模式指數據的高字節保存在內存的低地址

例如:12345( 0x3039 ) 的存儲順序是 0x3九、0x30

判斷大小端

方法一:

#include<stdio.h>
int main(int argc, char *argv[])
{
int i = 0x12345678;
char c = i;
if (c == 0x78)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}

方法二:

#include<stdio.h>
int main(void)
{
int a = 0x12345678;
char *p = (char *)&a;
if (0x78 == *p)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}

方法三:

#include<stdio.h>
typedef union NODE
{
int i;
char c;
}Node;
int main(int argc, char *argv[])
{
Node node;
node.i = 0x12345678;
if (0x78 == node.c)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}

上期第四題

#include<stdio.h>
int main()
{
float a = 12.5;
printf("%d\n", a);
printf("%d\n", (int)a);
printf("%d\n", *(int*)&a);
return 0;
}
  • printf("%d\n", a);

printf因爲類型不匹配,因此,會把float直接轉成double,double 8字節,12.5f轉成十六進制:0x4029 0000 000 00000

計算機(小端模式下)存儲的值爲:00 00 00 00 00 00 29 40

而咱們的%d要求是一個4字節的int,對於double的內存佈局,咱們能夠看到前四個字節是00,因此輸出天然是0了。

  • printf("%d\n", (int)a);

float強轉成int,省略掉小數部分,全部爲12

  • printf("%d\n", (int)&a);

float 4個字節,12.5f轉成二進制是:0100 0001 0100 1000 0000 0000 0000 0000,十六進制是:0x41480000,十進制是:1095237632

上期第10題

題目回顧:

#include<stdio.h>
int main()
{
int a = 3, b = 5;
printf(&a["Ya!Hello! how is this? %s\n"], &b["junk/super"]);
//等價printf("Hello! how is this? %s\n", "super");
printf(&a["WHAT%c%c%c %c%c %c !\n"], 1["this"], 2["beauty"], 0["tool"], 0["is"], 3["sensitive"], 4["CCCCCC"]);
return0;
}

指針的另類用法:

char arr[20] = "hello world";
printf("%s\n", arr); //從&arr[0]地址處開始讀取字符串到'\n'結束 輸出hello world
printf("%s\n", &arr[6]); //從&arr[6]地址處開始讀取字符串到'\n'結束 輸出world
printf("%s\n", &6[arr]); //從&arr[6]地址處開始讀取字符串到'\n'結束 輸出world

arr[i] 其實就是 *(arr+i)也就是 *(i+arr),這個屬於語法規則,只是用的少。

printf(&a["Ya!Hello! how is this? %s\n"], &b["junk/super"]);
//等價printf("Hello! how is this? %s\n", "super");

在這裏的"Ya!Hello! how is this? %s\n"是一個存儲在常量區的字符串

char* p = "Ya!Hello! how is this? %s\n";
char* p1 = "junk/super";
printf(&a[p], &b[p1]);
printf(&p[3], &p1[5]);

這裏的數字3,5就是地址的偏移量


關鍵字【內存 指針】


End



做者:夢凡

大家的在看就是對我最大的確定,
點個在看好嗎~


編程學習基地
常回基地看看

本文分享自微信公衆號 - 編程學習基地(LearnBase)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索