hello~親愛的看官老爺們新年好~相信很多同窗知道,若是要將一個數字轉換爲它的相反數,在 Javascript 中,除了在它前面加個-
號以外,還能夠對該數字進行取反,以後再加 1。前者(本質是 0 減去對應的數字)能夠獲得相反數,徹底符合咱們的直覺,但爲什麼取反加一也能夠,這看起來不太科學,本文將帶你一探究竟~編碼
友情提示,計算機科班的童鞋,對此應該是爛熟於心了,可能對你幫助有限。但不清楚其中細節的同窗,但願本文能知足你好奇心之餘,瞭解多一點二進制的知識。如下是正文~spa
估計各位童鞋確定聽過如下這條法則:code
減去一個數,等於加上這個數的相反數。ip
同時也應該瞭解,爲了區分正負數,二進制中的最高位是符號位,其中正數的符號位是 0,而負數的符號位爲 1。以 Java 的 byte 類型(8位,範圍是 -128 ~ 127,即 00000000
至 11111111
)爲例,若是按照直覺,既然最高位爲符號位,那麼 -1 應該表示爲 10000001
。想法很美好,現實卻很骨感。get
思考如下問題,儘管負數不肯定,但咱們確定正數 1 表示爲 00000001
,若是須要獲得算式 1-1
的結果 ,按照上面的法則,能夠將算式轉化爲 1+(-1)
進行運算,可是 10000001
+ 00000001
,不管怎麼進行運算,彷佛都很可貴到 00000000
這個值吧?因而可知,計算機中儲存負數的值,並非那麼簡單的~數學
負數的探索彷佛陷入了死衚衕,那讓咱們先把目光轉移一下,從屏幕轉到牆上的時鐘之上~假設這個鍾時針和分針能夠分別進行調整,互不影響,且如今時鐘顯示的時間是 10:40,但對比北京時間,快了 10 分鐘,那麼咱們改如何調整時鐘呢?簡單地作個算式:it
45
- 10
----------
35
複製代碼
得出的答案是 35,那麼將分針調整到 35 的位置便可,也不須要調整時針。很簡單對吧?那若是如今時間也是 10:40,但時間快了 15 分鐘,那該如何調整?你心中估計會想,那還不簡單,豎式同樣能算出來~但這裏加一個需求,不但願豎式中出現借位(也就是 運算 40-15
時,因爲被減數個位不如減數個位大,須要被減數從十位中借 1 到個位之上),那該如何實現呢?table
emmmmmmmm,彷佛比較麻煩~借位是因爲被減數的某一位不夠減數大而致使的,那若是被減數足夠大,彷佛就能解決這問題。原式是 40-15
,能夠等價改寫爲 40+(59-15)-59
,答案是徹底一致的。但這裏仍是有問題,運算兩次以後,剩下的算式爲 84-59
,仍然要借位不是嗎?那咱們再改寫一下:40+(59-15)+1-60
。這樣的算式運算下來,再也不須要借位了。換成時鐘操做,就是直接將分針調整到 25 便可。class
看到這裏,估計你心中會多了一點明悟,但彷佛其中還有一些說不過去的地方對吧?好比這個例子:如今的時間是 10:15,如今的時間比北京時間快了 40 分鐘,那該如何進行運算呢?按照上面的例子,能夠依樣寫下這條算式 15+(59-40)+1-60
,但問題來了,算到最後,是 35-60
,仍是要借位不是嗎?硬件
是,但能夠不是。-60
在時鐘上的本質是,將時針回撥一下。那麼 35-60
,其實能夠分解爲兩個操做,時針撥動到 35 的位置,時針回撥到 9 的位置,如今的時間爲 09:35,難道不是正確的答案嗎?
在恍然大悟前先停一下,還有一點點東西須要瞭解。相信數軸的概念銘刻在各位心中,它是徹底符合直覺的,且數軸是無窮的,相信你們也都知道,通常印象中的數軸以下:
-60, -59, -58, -57···, -2, -1, 0, 1, 2···57, 58, 59
但若是數軸是有範圍的話,假設總共只有 120 個整數在數軸上,若是咱們想消除負號,那麼用正數表示負數,也何嘗不可,好比以 59 爲分界,大於 59 的數均爲負數。即 -1 表示爲 119,-40 表示爲 80,-25 表示爲 95 等:
60, 61, 62, 63···118, 119, 0, 1, 2···57, 58, 59
那麼以前時鐘的例子,是以 59 做爲避免借位的被減數,但這是爲了方便時鐘撥動,這裏咱們再往前跨一步,剛纔時鐘的運算:15-40
能夠表示爲 15+80
,運算結果是 95,對錶查詢可得,95 的值表明的是 -25,運算正確!
你可能會吐槽減法是借位了,但主要是爲了方便映照時鐘的例子,換成 999 做爲避免借位的被減數,就是標準對 9 的補數。不妨在紙上畫一下,此時 -1 表示爲 999,-40 表示爲 959,-25 表示爲 974,15-40
即 015+959
,運算結果是 974,也是徹底對應上的。
有了上面的鋪墊,二進制的負數已經呼之欲出啦~符號位自然能夠做爲正負數的分割點。仍是以 Java 的 byte 類型爲例,8 位一共能夠表示 256 個數字,因爲 0 表示爲 00000000
,首位符號位也是 0,於是正數少一位,按照上一節的例子,推出當前序列爲:
10000001, 10000002···11111101, 11111110, 11111111, 00000000, 00000001···01111101, 01111110, 01111111
二進制數 | 十進制數 |
---|---|
10000000 | -128 |
10000001 | -127 |
10000002 | -126 |
··· | ··· |
11011000 | -40 |
··· | ··· |
11100111 | -25 |
··· | ··· |
00001111 | 15 |
··· | ··· |
01111111 | 127 |
那麼仍是這條算式:15-40
,二進制中即爲 00001111+11011000
,稍微數一下手指,得出答案是 11100111,對應的值爲 -25!然而,這個表背起來是沒意義的,那該如何運算呢?思考一下,以前運算 15-40
時,咱們轉換爲 15+(59-40)+1-60
,59 爲足夠大的被減數,在 byte 類型中,最大的數不會超過 11111111
(即 255),於是能夠轉換爲:
11111111(255)
- 00101000(40)
+ 00000001(1)
+ 00001111(15)
- 100000000(256)
複製代碼
二進制其實十分有趣,先觀察首先須要運算的 11111111-00101000
,結果是 11010111,也就是各位取反,下一步是加一,獲得結果是 11011000,不就是表中 -40 對應的值了麼?因此該表的推導是有嚴格的數學意義的,並非隨便編造一個表,到這裏,應該明白爲什麼在計算機中,取某個數的相反數是取反再加一了吧~
算式最後一步是減去 256,計算機中這步均可以省下了,由於 256 已經超過 byte 的範圍,直接忽略。同理,若是計算如 -1+1
之類的算式時,會獲得答案是 256 (即 100000000),但因爲超過範圍,首位 1 直接被忽略不計,於是得出的答案是 0 。以上運算,符號位均參與運算,並不須要區別對待,這對計算機而言是很是友好的。
爲嚴謹起見,補充兩個條件:
以上就是全文的內容啦,二進制相關的知識,實際上是至關有意思的,也許瞭解它們並不會使咱們的代碼能力日新月異,但保持好奇心,不斷探索未知的領域,必定是一個良好的習慣~
感謝各位看官大人看到這裏,知易行難,但願本文對你有所幫助~謝謝!