1. 程式人生 > >int與float深入理解

int與float深入理解

別在int與float上栽跟頭


int與float是我們每天程式設計都用的兩種型別,但是我們真的足夠了解它們嗎。昨天在部落格園看到一個比較老的笑話: “昨天晚上下班回家,一民警迎面巡邏而來。突然對我大喊:站住!民警:int 型別佔幾個位元組?  我:4 個。  民警:你可以走了。  我:為什麼問這樣的問題? 民警:深夜還在街上走,寒酸苦逼的樣子,不是小偷就是程式設計師。”(注:看到有朋友評論說佔幾個位元組跟具體的環境有關,學過C++的都知道,在C++這樣的語言中確實取決於環境,但是在Java跟C#中不管什麼環境都規定是4個位元組,所以後邊我們只討論4個位元組的情況)

        看完這個笑話,我腦袋立馬將float、double等型別的位元組長度閃了個遍。我知道float也佔4個位元組,但儲存結構跟int是不一樣的,並且表示範圍也不一樣。緊接著就出現了一個疑問,到底哪些int值是float不能表示的呢?如果你回答不了這個問題,那還是好好地瞭解一下吧,如果我說的不夠清楚,請多查點其他的資料看一下。

       為什麼有些int是float表示不了的呢?因為int與float同樣佔4個位元組,float表示的範圍又比int大並且還包含很多小數,那int的每個值都能被float表示就是不可能的事情了。在平時的程式設計中好像也沒有感覺什麼不對呀,這是為什麼呢?先把這個問題留到後邊,原理說清楚了再來回答這個問題。在文章的下邊帖了一個進位制轉換程式,方便大家使用。

小數十進位制與二進位制的轉換

二進位制轉換成十進位制:跟整數轉換一個原理,例如二進位制11.11轉換為十進位制 1*2^1+1*2^0+1*2^(-1)+1*2^(-2)=3.75。

        十進位制轉換成二進位制:整數部分不用說了,跟整數的十進位制轉成二進位制沒有區別。小數部分採用乘2取整的方式,比如3.75整數部分對應的二進位制是11。小數部分0.75,先乘以2等於1.5,取1.5的整數部分1。再用0.5(上次乘2的結果的小數部分)乘以2等於1.0,取1.0的整數部分1,現在已經沒有小數部分了,終止。0.75對應的二進位制就是.11。

        所以3.75對應的二進位制是11.11。注意這裡的3.75和1.11只是浮點數十進位制與二進位制的不同表示形式,儲存結構是一樣的,因為本來就是同一個數。記憶體結構又是怎麼樣的呢,下邊介紹。   二. float的儲存結構   float也是佔32位,第一位是符號位(sign),符號位後邊8位是指數(exponent),最後23位是尾數(mantissa)。

        float值的二進位制表示形式是:sign* mantissa* 2^exponent。注意這個表示式是對應上述儲存結構的二進位制。

        符號位,表述浮點數的正或者負,0代表正,1代表負。


        指數位,實際也是有正負的,但是沒有單獨的符號位,在計算機的世界裡,進位都是二進位制的,指數表示的也是2的N次冪,8位指數表達的範圍是0到255,而對應的實際的指數是-127到128。也就是說實際的指數等於指數位表示的數值減127。這裡特殊說明,-127和+128這兩個指數數值在IEEE當中是保留的用作多種用途的,這裡就不多做介紹了,有興趣的可以查閱其他資料。

        尾數位,只代表了二進位制的小數點後的部分,小數點前的那位被省略了,當指數位全部為0時省略的是0否則省略的是1,為什麼呢,看個例子:

        二進位制11.11表示成指數形式是1.111*2^1,0.1111表示成指數形式是1.111*2^(-1)。由此可見,正常情況下二進位制的指數形式是肯定有一個1的,所以儲存的時候直接省略。但是在指數位全部為0時,指數是-127,這個數字是有特殊含義的,在尾數全部為0時代表的數值是0,省略的那位是0,如果省略的是1那麼0這個數字就沒法用float表示了。

結合例子理解一下

那我們就看一下3.75的記憶體結構到底是什麼樣子的。首先轉化成二進位制形式11.11。轉化成二進位制指數形式1.111*21。由此我們可以得知尾數部分是111(將1省略掉了),不足23位的後邊補0,指數部分是1+127=128,對應二進位制10000000。所以儲存結構就是01000000011100000000000000000000。

        反過來轉換一下,比如某個float的儲存結構是01000000011100000000000000000000,符號位是正的,指數位是128,實際的指數是128-127=1,尾數是111,再加上省略的那位就是1.111。所以對應的二進位制指數形式是1.111*2^1,對應的二進位制是11.11,對應的十進位制是3.75。

        到這裡我們就可以看出,實際上尾數決定了浮點數的精度,尾數只有23位,加上省略的那位就是24位。如果一個int型別的值小於224,那麼float是完全可以表示的。如果int型別大於224就不一定能表示了。假如一個int數值的二進位制表示形式是100000000000000000000000,表示成指數形式是1.00000000000000000000000*2^23,對應的float的型別,尾數位全部為0,指數位是23+127=150,這樣完全沒有問題。假如一個int數值的二進位制表示形式是1000000000000000000000001,表示成指數形式是1.000000000000000000000001*2^24,對應的float的型別尾數位是000000000000000000000001一共24位,這樣就完全超出了float最多容納23位尾數的能力。所以就不能正確表達這個int值了。由此也可以得出不能被float準確表達的最小int值是2^24+1。我們再將1000000000000000000000001的值加1,變成了1000000000000000000000010,這樣變換為指數形式可以看出尾數又變為了23位,也就是說25位的二進位制整數最後一位是0才能被float準確表示,每2個數就有一個不能被準確表示。如果是26位的二進位制整數最後兩位都是0才可以被float準確表達,每4個數就有3個不能被準確表示,以此類推。

        現在再來回答為什麼在程式設計的過程中似乎沒怎麼引起注意,這是因為,我們平時用的數值基本都小於224+1=16777217。

java中的float和double的精度問題

1、背景知識
在java中沒有細講,只是講了float佔32位(bit),double佔 64位。 
對於計算機來說,用位數表示是合適的。但有人喜歡用位元組(byte)表示。一個位元組佔8位。 
1 byte = 8 bit. 
所以float佔4個位元組,double佔8個位元組。 
不過我還是喜歡用位數表示。這樣更直接,也更容易理解計算機是怎麼儲存這些型別的。 

對於精度和範圍,還是參考一下c++吧。 



2、儲存知識
計算器儲存浮點數的方法:(用科學計數法儲存) 
將要存的數先轉換為小數(0.xxxxxx)x10的n次冪的形式進行儲存。 
例如: 
3.1415 將被轉換為:0.31415 x 10^1 
100000 將被轉換為:0.1 x 10^6 

首先申明一點,先範圍(有效數字位,包括整數位和小數位),再精度。 



3、下面切入正題
===================== 
在c++中單精度float型別與雙精度double型別的問題 

【"單精度用float表示,在計算機中使用4位位元組(32位bit)來表示,具有7位有效數字"】 

float型別儲存的時候1個bit是符號位,8個bit是指數位,剩下的23個bit是有效數字位。 
2的23次方是8388608,即7位有效數字,精度(10進位制)。 

一個單精度的浮點數在記憶體當中佔用了32個bit位,按照浮點數的表示標準,最高位表示符號,這32位一部分用來表示階碼,一部分用來表示小數部分。 
按照這個表示方法轉化成10進位制之後,它能表示的最高精度是7位有效數字。

比如 
float a=3.14159;a在記憶體中實際上表示為0.314159乘以10的1次方(0是符號位),而分配給a的儲存單元就分為兩部分,一部分存0.314159,一部分存指數1,而且也都是轉化為2進位制來存。 

================== 
float ,1位符號位, 8位指數位,23位尾數位 
double,1位符號位,11位指數位,52位尾數位 

float尾數位23位,2^23=8.3E6,7位,所以不同的編譯器規定不同,有些是7位,有些8位 
double尾數52位,2^52=4.5E15,15位,所以double的有效位數是15位 


後記: 
數一下有效數字位數(整數位+小數位),7位以內的用float,15位以內的用double 
但是還有一點小小的區別: 
float f =  (float) 62345678.912345;  // => 6.234568E7  共 7 位 
float f2 =  (float) 12345678.912345; // => 1.2345679E7 共 8 位 

(精度問題,float精度為7--8位,8位的情況是第一位是1,當是2時進位後面的精度丟失?)




相關推薦

intfloat深入理解

別在int與float上栽跟頭 int與float是我們每天程式設計都用的兩種型別,但是我們真的足夠了解它們嗎。昨天在部落格園看到一個比較老的笑話: “昨天晚上下班回家,一民警迎面巡邏而來。突然對我大喊:站住!民警:int 型別佔幾個位元組?  我:4 個。  民警:你可

java多執行緒ThreadRunnable的區別使用深入理解

首先,多執行緒的實現方式兩種:一種是繼承Thread類,另一種是實現Runnable介面。 那麼這兩種方法的區別何在?該如何選擇? 第一:他們之間的關係 檢視J2EE的API看到 Thread類中:  public class Thread extends Objec

HashCode()equals()深入理解

1、hashCode()和equals()方法都是Object類提供的方法,     hashCode()返回該物件的雜湊碼值,該值通常是一個由該物件的內部地址轉換而來的int型整數,  Object的equals()方法等價於==,也就是判斷兩個引用的物件是否是同一物件,所謂同一物件就是

intunsigned等轉換深入理解(二)

1、隱式轉換C在以下四種情況下會進行隱式轉換:       1、算術運算式中,低型別能夠轉換為高型別。       2、賦值表示式中,右邊表示式的值自動隱式轉換為左邊變數的型別,並賦值給他。       3、函式呼叫中引數傳遞時,系統隱式地將實參轉換為形參的型別後,賦給形參

深入理解_JVM內存管理JDK監控工具故障處理工具07

最大 jhat hotspot interval 統計信息 傳遞 啟動 令行 相同 1、jps: JVM process Status Tool,顯示系統所有的HotSpot虛擬機進程以及進程的本地虛擬機的唯一ID(LVMID,Local Virtual Mac

深入理解JVM_內存管理對象訪問大小02

gms uwa hint map awd bpp ase pbo cdc 1、對象訪問: 在java語言中,對象訪問如何進行的? (1)最簡單的訪問,也會涉及java棧、java堆和方法區這三個最重要的內存區域之間的關聯關系。 Ob

深入理解JVM:垃圾收集器內存分配策略

四種 內存回收 第一次 不可達 append test 方法 static hot 堆裏面存放著Java世界差點兒全部的對象實例,垃圾收集器在對堆進行回收前。第一件事情就是要確定這些對象之中哪些還存活,哪些已經死去。推斷對象的生命周期是否結束有下面幾種方

深入理解Java虛擬機:JVM高級屬性最佳實踐》讀書筆記(更新中)

pen 內存區域 深度 span 進化 ria 最短 描述 core 第一章:走進Java 概述 Java技術體系 Java發展史 Java虛擬機發展史 1996年 JDK1.0,出現Sun Classic VM HotSpot VM, 它是 Sun JDK 和 Open

深入理解RESTServlet架構的區別

方法 增刪 ssi 困難 spring 客戶端 and 基於 www. 深入理解REST與Servlet架構的區別 本身這個比較是個偽命題,因為 RESTful Service是一個軟件架構“風格”, 而servlet是java 服務端的一種技術 之所以把它們拿出來

深入理解Java虛擬機——java內存區域內存溢出異常(一)

線程 文件的 語言 沒有 虛擬 深入理解java 十倍 並且 周期 Java虛擬機全稱:java virtual machine;是Java開發語言中,用來運行Java字節碼文件的平臺;通俗的講,就是一個程序。它提供對Java字節碼的解釋及運行,從而使Java語言能獨立於各

mybatis深入理解之 # $ 區別以及 sql 預編譯

tcl nec from esql 校驗 ntp code 理解 替換字符串 mybatis 中使用 sqlMap 進行 sql 查詢時,經常需要動態傳遞參數,例如我們需要根據用戶的姓名來篩選用戶時,sql 如下: select * from user where nam

深入理解Java類型信息(Class對象)反射機制

成員變量 字段 機制 () 程序 轉換 默認 數據 統一   深入理解Class對象    RRTI的概念以及Class對象作用    認識Class對象之前,先來了解一個概念,RTTI(Run-Time Type Identification)運行時類型識別,對於這個詞一

深入理解JVM之JVM內存區域內存分配

錯誤 銷毀 構造方法 初學 不存在 data 空閑 table fin 深入理解JVM之JVM內存區域與內存分配   在學習jvm的內存分配的時候,看到的這篇博客,該博客對jvm的內存分配總結的很好,同時也利用jvm的內存模型解釋了java程序中有關參數傳遞的問題。  

垃圾收集器內存分配策略 (深入理解JVM二)

nali noclass eth 清理 full gc 原因 商業 jit編譯器 代碼 1.概述 垃圾收集(Garbage Collection,GC). 當需要排查各種內存溢出、內存泄露問題時,當垃圾收集成為系統達到更高並發量的瓶頸時,我們就需要對這些&ldquo

深入理解PHP中賦值引用

str ring int 之前 不同 重新 small nts 計數 【原文】 先看下面的問題: 1 2 3 4 5 6 7 8 <?php $a = 10;//將常量值賦給變量,會為a分配內存空間 $b = $a;//變量賦值給變量,是不是

深入理解B/SC/S架構

模版 ima 替代 客戶端 不同的 onos 發展 均衡器 ear 深入理解B/S與C/S架構閱讀目錄C/S架構簡要介紹什麽是B/S架構B/S架構的幾種形式發展前景其實B/S架構是屬於後臺方面的東西,不過作為一個前端,也是需要了解一下滴C/S架構簡要介紹在了解什麽是B/S架

深入理解Java虛擬機:JVM高級特性最佳實踐》【PDF】下載

構建 實現原理 jvm內存 本地方法棧 人物 lvm 劃分 同時 棧溢出 《深入理解Java虛擬機:JVM高級特性與最佳實踐》【PDF】下載鏈接: https://u253469.pipipan.com/fs/253469-230062566 內容簡介 作為一位j

ThreadLocal深入理解內存泄露分析

fonts statistic rac jdk 占用內存 得出 銷毀 prev other ThreadLocal 當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本

深入理解Java虛擬機》——垃圾收集器內存分配策略

特點 兩個 instance 統一 tro 過程 引用計數 分析算法 效率問題 GC需要完成: 哪些內存需要回收 什麽時候回收 如何回收 如何確定對象不再使用 引用計數算法 給對象添加一個引用計數器,當有一個地方引用它時,計數器值進行加1操作;當引用失效時,計數器值

[數據庫事務鎖]詳解七: 深入理解樂觀鎖悲觀鎖

ood insert 影響 hiberna memcach begin 策略 goods 其它 註明: 本文轉載自http://www.hollischuang.com/archives/934在數據庫的鎖機制中介紹過,數據庫管理系統(DBMS)中的並發控制的任務是確保在