Java基礎-一文搞懂位運算

来源:https://www.cnblogs.com/iou123lg/archive/2018/09/03/9576468.html
-Advertisement-
Play Games

在日常的Java開發中,位運算使用的不多,使用的更多的是算數運算(+、-、*、/、%)、關係運算(<、>、<=、>=、==、!=)和邏輯運算(&&、||、!),所以相對來說對位運算不是那麼熟悉,本文將以Java的位運算來詳細介紹下位運算及其應用。 1、 位運算起源 位運算起源於C語言的低級操作,Ja ...


  在日常的Java開發中,位運算使用的不多,使用的更多的是算數運算(+、-、*、/、%)、關係運算(<、>、<=、>=、==、!=)和邏輯運算(&&、||、!),所以相對來說對位運算不是那麼熟悉,本文將以Java的位運算來詳細介紹下位運算及其應用。

1、 位運算起源

  位運算起源於C語言的低級操作,Java的設計初衷是嵌入到電視機頂盒內,所以這種低級操作方式被保留下來。所謂的低級操作,是因為位運算的操作對象是二進位位,但是這種低級操作對電腦而言是非常簡單直接,友好高效的。在簡單的低成本處理器上,通常位運算比除法快得多,比乘法快幾倍,有時比加法快得多。雖然由於較長的指令流水線和其他架構設計選擇,現代處理器通常執行加法和乘法的速度與位運算一樣快,但由於資源使用減少,位運算通常會使用較少的功率,所以在一些Java底層演算法中,巧妙的使用位運算可以大量減少運行開銷。

2、 位運算詳解

  Java位運算細化劃分可以分為按位運算和移位運算,見下表。

細化

符號

描述

運算規則

按位運算

&

兩位都為1,那麼結果為1

|

有一位為1,那麼結果為1

~

~0 = 1,~1 = 0

^

異或

兩位不相同,結果為1

移位運算

<< 

左移

各二進位位全部左移N位,高位丟棄,低位補0

>> 

右移

各二進位位全部右移N位,若值為正,則在高位插入 0,若值為負,則在高位插入 1

>>> 

無符號右移

各二進位位全部右移N位,無論正負,都在高位插入0

  在進行位運算詳解之前,先來普及下電腦中數字的表示方法。對於電腦而言,萬物皆0、1,所有的數字最終都會轉換成0、1的表示,有3種體現形式,分別是:原碼、反碼和補碼

  原碼:原碼表示法在數字前面增加了一位符號位,即最高位為符號位,正數位該位為0,負數位該位為1.比如十進位的5如果用8個二進位位來表示就是00000101,-5就是10000101。

  反碼:正數的反碼是其本身,負數的反碼在其原碼的基礎上,符號位不變,其餘各個位取反。5的反碼就是00000101,而-5的則為11111010。

  補碼:正數的補碼是其本身,負數的補碼在其原碼的基礎上,符號位不變,其餘各位取反,最後+1。即在反碼的基礎上+1。5的反碼就是00000101,而-5的則為11111011。

  瞭解了這幾個概念後,我們現在先記住一個結論,那就是在電腦系統中,數字一律用補碼來表示、運算和存儲,具體的原因可以看這篇文章的討論,這裡不做更多討論,因為不是本文的重點。

2.1 與運算(&)

  規則:轉為二進位後,兩位為1,則結果為1,否則結果為0。

  舉例:

十進位

二進位(正數原碼、反碼、補碼一致)

10

00000000000000000000000000001010

&12

&00000000000000000000000000001100

=

=

8

00000000000000000000000000001000

 

十進位

二進位(原碼)

-6

10000000000000000000000000000110

&-2

&10000000000000000000000000000010

十進位

二進位(反碼)

-6

11111111111111111111111111111001

&-2

&11111111111111111111111111111101

十進位

二進位(補碼)

-6

11111111111111111111111111111010

&-2

&11111111111111111111111111111110

=

=

-6

11111111111111111111111111111010

  最後的計算結果11111111111111111111111111111010還是補碼的形式,要看其十進位,還需要先轉成二進位原碼。

  先轉反碼:11111111111111111111111111111010-1=11111111111111111111111111111001,得反碼11111111111111111111111111111001。

  再轉原碼:在反碼的基礎上轉原碼,符號位不變,其他各位取反,得10000000000000000000000000000110。第一位1代表負數,後面0110轉成十進位是6,得-6。

2.2 或運算(|)

  規則:轉為二進位後,有一位為1,則結果為1,否則結果為0。

  舉例:

十進位

二進位(正數原碼、反碼、補碼一致)

10

00000000000000000000000000001010

|12

|00000000000000000000000000001100

=

=

14

00000000000000000000000000001110

 

十進位

二進位(原碼)

-6

10000000000000000000000000000110

|-2

|10000000000000000000000000000010

十進位

二進位(反碼)

-6

11111111111111111111111111111001

|-2

|11111111111111111111111111111101

十進位

二進位(補碼)

-6

11111111111111111111111111111010

|-2

|11111111111111111111111111111110

=

=

-2

11111111111111111111111111111110

 

2.3 非運算(~)

  規則:轉為二進位後,~0 = 1,~1 = 0。

  舉例:

十進位

二進位(正數原碼、反碼、補碼一致)

~7

~00000000000000000000000000000111

=

=

-8

11111111111111111111111111111000(補碼需轉換為原碼)

  11111111111111111111111111111000-1得反碼,可以把1000看成是0112,得反碼11111111111111111111111111110111。根據反碼得原碼10000000000000000000000000001000。

十進位

二進位(原碼)

~(-6)

~10000000000000000000000000000110

十進位

二進位(反碼)

~(-6)

~11111111111111111111111111111001

十進位

二進位(補碼)

~(-6)

~11111111111111111111111111111010

=

=

5

00000000000000000000000000000101(正數原碼、反碼、補碼一致)

 

2.4 異或運算(^)

  規則:轉為二進位後,兩位不相同,結果為1,否則為0。

  舉例:

十進位

二進位(正數原碼、反碼、補碼一致)

15^2

00000000000000000000000000001111

^00000000000000000000000000000010

=

=

13

00000000000000000000000000001101

 

2.5 左移運算(<<)

  規則:轉為二進位後,各二進位位全部左移N位,高位丟棄,低位補0。

  舉例:

十進位

二進位(正數原碼、反碼、補碼一致)

2<<2

00000000000000000000000000000010

=

0000000000000000000000000000001000

8

00000000000000000000000000001000

 

十進位

二進位(先取補碼 再對補碼操作位移)

-2<<2

10000000000000000000000000000010(原碼)

 

11111111111111111111111111111101(反碼)

 

11111111111111111111111111111110(補碼)

 

1111111111111111111111111111111000

 

11111111111111111111111111111000(補碼)

 

11111111111111111111111111110111(反碼)

-8

10000000000000000000000000001000(原碼)

 

2.6 右移運算(>>)

  規則:轉為二進位後,各二進位位全部右移N位,若值為正,則在高位插入 0,若值為負,則在高位插入 1。

  舉例:

十進位

二進位(正數原碼、反碼、補碼一致)

2>>2

00000000000000000000000000000010

=

0000000000000000000000000000000010

0

00000000000000000000000000000000

 

十進位

二進位(先取補碼 再對補碼操作位移)

-6>>2

10000000000000000000000000000110(原碼)

 

11111111111111111111111111111001(反碼)

 

11111111111111111111111111111010(補碼)

 

1111111111111111111111111111111010

 

11111111111111111111111111111110(補碼)

 

11111111111111111111111111111101(反碼)

-2

10000000000000000000000000000010(原碼)

 

2.7 無符號右移運算(>>>)

  規則:轉為二進位後,各二進位位全部右移N位,無論正負,都在高位插入0。

  舉例:

十進位

二進位(先取補碼 再對補碼操作位移)

-1>>>1

10000000000000000000000000000001(原碼)

 

11111111111111111111111111111110(反碼)

 

11111111111111111111111111111111(補碼)

 

011111111111111111111111111111111

 

01111111111111111111111111111111(補碼)

 

01111111111111111111111111111110(反碼)

溢出,只能表示到int的最大值2147483647

10000000000000000000000000000001(原碼)

 

3、 應用

3.1 不用額外的變數實現兩個數字互換

  見參考資料中的BitOperationTest,方法reverse通過三次異或操作完成了兩個變數值的替換。

  證明很簡單,我們只需要明白異或運算滿足下麵規律(實際不止如下規律):

  0^a = a,a^a = 0;

  a ^ b = b ^ a;

  a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c;

  a ^ b ^ a = b;

  假設a,b兩個變數,經過如下步驟完成值交換:a=a^b,b=b^a,a=a^b。

  證明如下:

  因為a ^ b = b ^ a,又a=a^b,b=b^a。故b=b^a= b^ (a^b)=a。

  繼續a=a^b,a=(a^b) ^ b^ (a^b),故a=b。完成值交換。

3.2 不用判斷語句實現求絕對值

  公式如下:(a^(a>>31))-(a>>31)

  先整理一下使用位運算取絕對值的思路:若a為正數,則不變,需要用異或0保持的特點;若a為負數,則其補碼為原碼翻轉每一位後+1,先求其原碼,補碼-1後再翻轉每一位,此時需要使用異或1具有翻轉的特點。

  任何正數右移31後只剩符號位0,最終結果為0,任何負數右移31後也只剩符號位1,溢出的31位截斷,空出的31位補符號位1,最終結果為-1.右移31操作可以取得任何整數的符號位。

  那麼綜合上面的步驟,可得到公式。a>>31取得a的符號,若a為正數,a>>31等於0,a^0=a,不變;若a為負數,a>>31等於-1 ,a^-1翻轉每一位。

3.3 判斷一個數的奇偶性

  通過與運算判斷奇偶數,偽代碼如下:

  n&1 == 1?”奇數”:”偶數”

  奇數最低位肯定是1,而1的二進位最低位也是1,其他位都是0,所以所有奇數和1與運算結果肯定是1。

 

參考資料:

https://github.com/lingjiango/ConcurrentProgramPractice

https://en.wikipedia.org/wiki/Bitwise_operation

https://www.zhihu.com/question/20159860


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JavaScript 的核心概念主要由 語法、變數、數據類型 、操作符、語句、函數組成,這篇文章主要講解的是前面三個,後面三個下一篇文章再講解。 01 語法 熟悉 JavaScript 歷史的人應該都知道,JavaScript 的很多語法和 Java、C 語言類似,所以一些做後端的程式員上手前端很快 ...
  • 原文鏈接:http://blog.csdn.net/zhangerqing 設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的 ...
  • ThreadLocal 概述 ThreadLocal實例僅作為線程局部變數的==操作類==,以及==線程存儲局部變數時的Key==。真正的線程局部變數是存儲在各自線程的本地,通過Thread類中的 進行存儲。 若希望線上程本地存儲多個局部變數需要使用多個ThreadLocal實例進行操作。 Thre ...
  • https://www.cnblogs.com/jiahaoJAVA/p/6244278.html 1 什麼是redis? Redis 是一個基於記憶體的高性能key-value資料庫。 (有空再補充,有理解錯誤或不足歡迎指正) 2 Reids的特點 Redis本質上是一個Key-Value類型的記憶體 ...
  • 題意 約翰要帶N(1≤N≤100000)只牛去參加集會裡的展示活動,這些牛可以是牡牛,也可以是牝牛.牛們要站成一排.但是牡牛是好鬥的,為了避免牡牛鬧出亂子,約翰決定任意兩隻牡牛之間至少要有K(O≤K<N)只牝牛. 請計算一共有多少種排隊的方法.所有牡牛可以看成是相同的,所有牝牛也一樣.答案對5000 ...
  • 俗話說 「不要重覆造輪子」,關於是否有必要不再本次討論範圍。 創建這個項目的主要目的還是提升自己,看看和知名類開源項目的差距以及學習優秀的開源方式。 ...
  • 持續集成(Continuous Integration)指的是,頻繁地(一天多次)將代碼集成到主幹。 持續集成的目的,就是讓產品可以快速迭代,同時還能保持高質量。 它的核心措施是,代碼集成到主幹之前,必須通過自動化測試。只要有一個測試用例失敗,就不能集成。 持續集成可以把工程師從繁瑣的任務中解放出來 ...
  • fail-fast機制即為快速失敗機制,個人認為是一種防護措施,在集合結構發生改變的時候,使盡全力拋出ConcurrentModificationException,所以該機制大部分用途都是用來檢測Bug的; 下麵的代碼可以引發fail-fast fail-fast原理 每個集合都會實現可遍歷的介面 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...