Toán tử dịch chuyển bitwise (bit-shift) là gì và chúng hoạt động như thế nào?


1382

Tôi đã cố gắng học C trong thời gian rảnh rỗi và các ngôn ngữ khác (C #, Java, v.v.) có cùng một khái niệm (và thường là cùng các toán tử) ...

Những gì tôi đang tự hỏi là, ở một mức độ cốt lõi, những gì hiện-bit thay đổi ( <<, >>, >>>) làm, những vấn đề này có thể giúp giải quyết, và những gì gotchas ẩn nấp xung quanh uốn cong? Nói cách khác, hướng dẫn tuyệt đối cho người mới bắt đầu để thay đổi tất cả sự tốt đẹp của nó.


2
Các trường hợp chức năng hoặc không có chức năng mà bạn sẽ sử dụng bẻ khóa trong 3GL là rất ít.
Troy DeMonbreun

15
Sau khi đọc những câu trả lời này, bạn có thể muốn xem các liên kết sau: Graphics.stanford.edu/~seander/bithacks.html & jjj.de/bitwizardry/bitwizardrypage.html
vuốt

1
Điều quan trọng cần lưu ý là việc dịch chuyển bit cực kỳ dễ dàng và nhanh chóng cho máy tính. Bằng cách tìm cách sử dụng dịch chuyển bit trong chương trình của bạn, bạn có thể giảm đáng kể thời gian sử dụng bộ nhớ và thời gian thực hiện.
Hoytman

@Hoytman: Nhưng lưu ý rằng các trình biên dịch tốt đã biết nhiều thủ thuật này và thường tốt hơn trong việc nhận ra nơi nào có ý nghĩa.
Sebastian Mach

Câu trả lời:


1713

Các toán tử dịch chuyển bit làm chính xác những gì tên của chúng ngụ ý. Họ thay đổi bit. Đây là một giới thiệu ngắn gọn (hoặc không quá ngắn gọn) cho các toán tử thay đổi khác nhau.

Người vận hành

  • >> là toán tử dịch chuyển phải số học (hoặc đã ký).
  • >>> là toán tử dịch chuyển phải logic (hoặc không dấu).
  • << là toán tử dịch chuyển trái, và đáp ứng nhu cầu của cả hai dịch chuyển logic và số học.

Tất cả những nhà khai thác có thể được áp dụng cho các giá trị số nguyên ( int, long, có thể shortbytehay char). Trong một số ngôn ngữ, áp dụng toán tử shift cho bất kỳ kiểu dữ liệu nào nhỏ hơn inttự động thay đổi kích thước toán hạng thành một int.

Lưu ý rằng đó <<<không phải là một toán tử, bởi vì nó sẽ là dự phòng.

Cũng lưu ý rằng C và C ++ không phân biệt giữa các toán tử dịch chuyển phải . Họ chỉ cung cấp >>toán tử và hành vi dịch chuyển phải được triển khai được xác định cho các loại đã ký. Phần còn lại của câu trả lời sử dụng các toán tử C # / Java.

(Trong tất cả chủ đạo C và C ++ triển khai bao gồm GCC và Clang / LLVM, >>trên các loại ký là số học Một số mã giả định này, nhưng nó không phải là một cái gì đó bên nhận bảo lãnh tiêu chuẩn Nó không.. Undefined , mặc dù, tiêu chuẩn đòi hỏi triển khai để xác định nó trở thành một Cách này hay cách khác. Tuy nhiên, các dịch chuyển trái của các số có chữ ký âm hành vi không xác định (tràn số nguyên đã ký). Vì vậy, trừ khi bạn cần dịch chuyển sang phải số học, thường nên thực hiện dịch chuyển bit của bạn với các loại không dấu.)


Ca trái (<<)

Các số nguyên được lưu trữ, trong bộ nhớ, dưới dạng một chuỗi các bit. Ví dụ: số 6 được lưu dưới dạng 32 bit intsẽ là:

00000000 00000000 00000000 00000110

Chuyển mẫu bit này sang vị trí bên trái ( 6 << 1) sẽ dẫn đến số 12:

00000000 00000000 00000000 00001100

Như bạn có thể thấy, các chữ số đã dịch chuyển sang trái theo một vị trí và chữ số cuối cùng bên phải được điền bằng 0. Bạn cũng có thể lưu ý rằng dịch chuyển sang trái tương đương với nhân với lũy thừa của 2. Vì vậy, 6 << 1tương đương với 6 * 2, và 6 << 3tương đương với 6 * 8. Một trình biên dịch tối ưu hóa tốt sẽ thay thế phép nhân bằng ca khi có thể.

Dịch chuyển không tròn

Xin lưu ý rằng đây không phải là ca tròn. Chuyển giá trị này sang trái theo một vị trí ( 3,758,096,384 << 1):

11100000 00000000 00000000 00000000

kết quả trong 3.221.225.472:

11000000 00000000 00000000 00000000

Chữ số bị dịch chuyển "khỏi cuối" bị mất. Nó không quấn quanh.


Chuyển đúng logic (>>>)

Một sự thay đổi hợp lý bên phải là sự thay đổi đối với sự dịch chuyển trái. Thay vì di chuyển các bit sang trái, chúng chỉ đơn giản là di chuyển sang phải. Ví dụ: thay đổi số 12:

00000000 00000000 00000000 00001100

ở bên phải bởi một vị trí ( 12 >>> 1) sẽ lấy lại 6 gốc của chúng tôi:

00000000 00000000 00000000 00000110

Vì vậy, chúng ta thấy rằng dịch chuyển sang phải tương đương với sự phân chia theo quyền hạn của 2.

Mất bit đã biến mất

Tuy nhiên, một sự thay đổi không thể lấy lại các bit "bị mất". Ví dụ: nếu chúng ta thay đổi mẫu này:

00111000 00000000 00000000 00000110

ở bên trái 4 vị trí ( 939,524,102 << 4), chúng tôi nhận được 2.147.483.744:

10000000 00000000 00000000 01100000

và sau đó dịch chuyển trở lại ( (939,524,102 << 4) >>> 4), chúng tôi nhận được 134,217,734:

00001000 00000000 00000000 00000110

Chúng tôi không thể lấy lại giá trị ban đầu của mình một khi chúng tôi đã mất bit.


Số học phải dịch chuyển (>>)

Sự dịch chuyển số học đúng giống như sự dịch chuyển hợp lý, ngoại trừ thay vì đệm bằng 0, nó thay đổi với bit đáng kể nhất. Điều này là do bit có ý nghĩa nhất là bit dấu , hoặc bit phân biệt số dương và số âm. Bằng cách đệm với bit có ý nghĩa nhất, sự thay đổi bên phải của số học là bảo toàn dấu hiệu.

Ví dụ: nếu chúng ta giải thích mẫu bit này là số âm:

10000000 00000000 00000000 01100000

chúng ta có số -2,147,483,552. Chuyển vị trí này sang đúng 4 vị trí với dịch chuyển số học (-2,147,483,552 >> 4) sẽ cho chúng ta:

11111000 00000000 00000000 00000110

hoặc số -134,217,722.

Vì vậy, chúng tôi thấy rằng chúng tôi đã bảo toàn dấu hiệu của các số âm của mình bằng cách sử dụng dịch chuyển phải số học, thay vì dịch chuyển đúng logic. Và một lần nữa, chúng ta thấy rằng chúng ta đang thực hiện phân chia theo quyền hạn của 2.


304
Câu trả lời sẽ làm rõ hơn rằng đây là một câu trả lời dành riêng cho Java. Không có toán tử >>> trong C / C ++ hoặc C #, và có hay không >> tuyên truyền dấu hiệu được triển khai được xác định trong C / C ++ (một gotcha tiềm năng chính)
Michael Burr

56
Câu trả lời là hoàn toàn không chính xác trong ngữ cảnh của ngôn ngữ C. Không có sự phân chia có ý nghĩa thành các thay đổi "số học" và "logic" trong C. Trong C, các ca làm việc như mong đợi trên các giá trị không dấu và trên các giá trị được ký dương - chúng chỉ thay đổi bit. Trên các giá trị âm, dịch chuyển phải được xác định thực hiện (nghĩa là không thể nói gì về những gì nó làm nói chung) và dịch chuyển trái chỉ bị cấm - nó tạo ra hành vi không xác định.
AnT

10
Audrey, chắc chắn có một sự khác biệt giữa dịch chuyển số học và logic phải. C chỉ đơn giản là để thực hiện lựa chọn được xác định. Và thay đổi trái trên các giá trị tiêu cực chắc chắn không bị cấm. Chuyển 0xff000000 sang trái một bit và bạn sẽ nhận được 0xfe000000.
Công viên Derek

16
A good optimizing compiler will substitute shifts for multiplications when possible. Gì? Bitshifts là bậc độ lớn nhanh hơn khi nó đi xuống đến các hoạt động ở mức độ thấp của một CPU, một tốt việc tối ưu hóa trình biên dịch sẽ làm chính xác ngược lại, có nghĩa là, chuyển phép nhân thông thường bởi quyền hạn của hai vào thay đổi chút.
Mahn

55
@Mahn, bạn đang đọc nó ngược từ ý định của tôi. Thay thế Y cho X có nghĩa là thay thế X bằng Y. Y là thay thế cho X. Vì vậy, sự thay đổi là thay thế cho phép nhân.
Công viên Derek

209

Giả sử chúng ta có một byte đơn:

0110110

Áp dụng một bithift bên trái duy nhất giúp chúng tôi:

1101100

Số 0 ngoài cùng bên trái đã được chuyển ra khỏi byte và một số 0 mới được thêm vào đầu bên phải của byte.

Các bit không cuộn qua; chúng bị loại bỏ. Điều đó có nghĩa là nếu bạn rời ca 1101100 và sau đó chuyển sang phải, bạn sẽ không nhận được kết quả tương tự.

Chuyển sang trái bởi N tương đương với nhân với 2 N .

Chuyển sang phải theo N là (nếu bạn đang sử dụng phần bù của người đó ) tương đương với việc chia cho 2 N và làm tròn thành số không.

Bitshifting có thể được sử dụng để nhân và chia cực kỳ nhanh, miễn là bạn đang làm việc với sức mạnh là 2. Hầu như tất cả các thói quen đồ họa cấp thấp đều sử dụng bẻ khóa.

Ví dụ, trở lại thời xưa, chúng tôi đã sử dụng chế độ 13h (320x200 256 màu) cho các trò chơi. Ở Chế độ 13h, bộ nhớ video được bố trí liên tục trên mỗi pixel. Điều đó có nghĩa là để tính toán vị trí cho một pixel, bạn sẽ sử dụng phép toán sau:

memoryOffset = (row * 320) + column

Bây giờ, trở lại thời đại ngày nay, tốc độ rất quan trọng, vì vậy chúng tôi sẽ sử dụng bithifts để thực hiện thao tác này.

Tuy nhiên, 320 không phải là sức mạnh của hai, vì vậy để giải quyết vấn đề này, chúng ta phải tìm ra sức mạnh của hai thứ được cộng lại với nhau tạo nên 320:

(row * 320) = (row * 256) + (row * 64)

Bây giờ chúng ta có thể chuyển đổi nó thành ca trái:

(row * 320) = (row << 8) + (row << 6)

Để có kết quả cuối cùng của:

memoryOffset = ((row << 8) + (row << 6)) + column

Bây giờ chúng tôi nhận được phần bù giống như trước đây, ngoại trừ thay vì thao tác nhân đắt tiền, chúng tôi sử dụng hai bithifts ... trong x86, nó sẽ giống như thế này (lưu ý, đó là mãi mãi kể từ khi tôi thực hiện lắp ráp (lưu ý của biên tập viên: đã sửa một vài lỗi và thêm một ví dụ 32 bit)):

mov ax, 320; 2 cycles
mul word [row]; 22 CPU Cycles
mov di,ax; 2 cycles
add di, [column]; 2 cycles
; di = [row]*320 + [column]

; 16-bit addressing mode limitations:
; [di] is a valid addressing mode, but [ax] isn't, otherwise we could skip the last mov

Tổng cộng: 28 chu kỳ trên bất kỳ CPU cổ nào có các thời gian này.

Vrs

mov ax, [row]; 2 cycles
mov di, ax; 2
shl ax, 6;  2
shl di, 8;  2
add di, ax; 2    (320 = 256+64)
add di, [column]; 2
; di = [row]*(256+64) + [column]

12 chu kỳ trên cùng một CPU cổ.

Có, chúng tôi sẽ làm việc chăm chỉ để loại bỏ 16 chu kỳ CPU.

Ở chế độ 32 hoặc 64 bit, cả hai phiên bản đều ngắn hơn và nhanh hơn rất nhiều. Các CPU thực thi không theo thứ tự hiện đại như Intel Skylake (xem http://agner.org/optizes/ ) có nhân phần cứng rất nhanh (độ trễ thấp và thông lượng cao), do đó mức tăng nhỏ hơn nhiều. AMD Bulldozer-Family chậm hơn một chút, đặc biệt là nhân 64 bit. Trên CPU Intel và AMD Ryzen, hai ca làm việc có độ trễ thấp hơn một chút nhưng nhiều hướng dẫn hơn bội số (có thể dẫn đến thông lượng thấp hơn):

imul edi, [row], 320    ; 3 cycle latency from [row] being ready
add  edi, [column]      ; 1 cycle latency (from [column] and edi being ready).
; edi = [row]*(256+64) + [column],  in 4 cycles from [row] being ready.

so với

mov edi, [row]
shl edi, 6               ; row*64.   1 cycle latency
lea edi, [edi + edi*4]   ; row*(64 + 64*4).  1 cycle latency
add edi, [column]        ; 1 cycle latency from edi and [column] both being ready
; edi = [row]*(256+64) + [column],  in 3 cycles from [row] being ready.

Trình biên dịch sẽ làm điều này cho bạn: Xem cách GCC, Clang và Microsoft Visual C ++ đều sử dụng shift + lea khi tối ưu hóareturn 320*row + col; .

Điều thú vị nhất cần lưu ý ở đây là x86 có một lệnh shift-and-add ( LEA) có thể thực hiện các dịch chuyển trái nhỏ và thêm vào cùng một lúc, với hiệu suất như một addhướng dẫn. ARM thậm chí còn mạnh hơn: một toán hạng của bất kỳ lệnh nào có thể được dịch chuyển sang trái hoặc phải miễn phí. Vì vậy, nhân rộng theo hằng số thời gian biên dịch được gọi là công suất 2 có thể còn hiệu quả hơn nhiều lần.


OK, trở lại thời hiện đại ... một cái gì đó hữu ích hơn bây giờ sẽ là sử dụng bithifting để lưu trữ hai giá trị 8 bit trong một số nguyên 16 bit. Ví dụ: trong C #:

// Byte1: 11110000
// Byte2: 00001111

Int16 value = ((byte)(Byte1 >> 8) | Byte2));

// value = 000011111110000;

Trong C ++, trình biên dịch sẽ làm điều này cho bạn nếu bạn đã sử dụng một structvới hai thành viên 8 bit, nhưng trong thực tế, chúng không phải lúc nào cũng vậy.


8
Mở rộng điều này, trên các bộ xử lý Intel (và nhiều bộ khác) để thực hiện việc này nhanh hơn: int c, d; c = d << 2; Hơn thế này: c = 4 * d; Đôi khi, ngay cả "c = d << 2 + d << 1" cũng nhanh hơn "c = 6 * d" !! Tôi đã sử dụng các thủ thuật này một cách rộng rãi cho các chức năng đồ họa trong thời đại DOS, tôi không nghĩ chúng hữu ích nữa ...
Joe Pineda

5
@James: không hoàn toàn, ngày nay, đúng hơn là phần sụn của card màn hình bao gồm mã như thế, được thực thi bởi GPU chứ không phải CPU. Vì vậy, về mặt lý thuyết, bạn không cần phải triển khai mã như thế này (hoặc như hàm gốc nghịch đảo ma thuật đen của Carmack) cho các chức năng đồ họa :-)
Joe Pineda

3
@JoePineda @james Các nhà văn trình biên dịch chắc chắn đang sử dụng chúng. Nếu bạn viết c=4*dbạn sẽ nhận được một ca. Nếu bạn viết k = (n<0)có thể được thực hiện với ca làm việc: k = (n>>31)&1để tránh một chi nhánh. Điểm mấu chốt, sự cải thiện về tính thông minh của trình biên dịch này có nghĩa là bây giờ không cần thiết phải sử dụng các thủ thuật này trong mã C và chúng làm ảnh hưởng đến tính dễ đọc và tính di động. Vẫn rất tốt để biết họ nếu bạn đang viết ví dụ mã vector SSE; hoặc bất kỳ tình huống nào bạn cần nó nhanh chóng và có một mẹo mà trình biên dịch không sử dụng (ví dụ mã GPU).
greggo

2
Một ví dụ điển hình khác: điều rất phổ biến là if(x >= 1 && x <= 9)có thể được thực hiện khi if( (unsigned)(x-1) <=(unsigned)(9-1)) Thay đổi hai bài kiểm tra có điều kiện thành một có thể là một lợi thế tốc độ lớn; đặc biệt là khi nó cho phép thực hiện xác nhận thay vì các nhánh. Tôi đã sử dụng điều này trong nhiều năm (nơi hợp lý) cho đến khi tôi nhận thấy khoảng 10 năm trước rằng các trình biên dịch đã bắt đầu thực hiện chuyển đổi này trong trình tối ưu hóa, sau đó tôi dừng lại. Vẫn cần biết, vì có những tình huống tương tự trong đó trình biên dịch không thể thực hiện chuyển đổi cho bạn. Hoặc nếu bạn đang làm việc trên một trình biên dịch.
greggo

3
Có một lý do mà "byte" của bạn chỉ có 7 bit?
Mason Watmough

104

Các hoạt động bitwise, bao gồm cả dịch chuyển bit, là cơ bản cho phần cứng cấp thấp hoặc lập trình nhúng. Nếu bạn đọc một đặc tả cho một thiết bị hoặc thậm chí một số định dạng tệp nhị phân, bạn sẽ thấy các byte, từ và dwords, được chia thành các bitfield không liên kết byte, chứa các giá trị quan tâm khác nhau. Truy cập các trường bit này để đọc / ghi là cách sử dụng phổ biến nhất.

Một ví dụ thực tế đơn giản trong lập trình đồ họa là pixel 16 bit được biểu diễn như sau:

  bit | 15| 14| 13| 12| 11| 10| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1  | 0 |
      |       Blue        |         Green         |       Red          |

Để có được giá trị xanh, bạn sẽ làm điều này:

 #define GREEN_MASK  0x7E0
 #define GREEN_OFFSET  5

 // Read green
 uint16_t green = (pixel & GREEN_MASK) >> GREEN_OFFSET;

Giải trình

Để có được giá trị CHỈ màu xanh lục, bắt đầu ở phần bù 5 và kết thúc ở mức 10 (tức là dài 6 bit), bạn cần sử dụng mặt nạ (bit), khi áp dụng cho toàn bộ pixel 16 bit, sẽ mang lại chỉ các bit chúng ta quan tâm.

#define GREEN_MASK  0x7E0

Mặt nạ thích hợp là 0x7E0, trong nhị phân là 0000011111100000 (tức là năm 2016 ở dạng thập phân).

uint16_t green = (pixel & GREEN_MASK) ...;

Để áp dụng mặt nạ, bạn sử dụng toán tử AND (&).

uint16_t green = (pixel & GREEN_MASK) >> GREEN_OFFSET;

Sau khi áp dụng mặt nạ, bạn sẽ kết thúc với số 16 bit, đây thực sự chỉ là số 11 bit vì MSB của nó nằm ở bit thứ 11. Màu xanh lá cây thực sự chỉ dài 6 bit, vì vậy chúng ta cần thu nhỏ nó xuống bằng cách sử dụng dịch chuyển phải (11 - 6 = 5), do đó sử dụng 5 làm offset ( #define GREEN_OFFSET 5).

Cũng phổ biến là sử dụng dịch chuyển bit để nhân nhanh và chia theo lũy thừa 2:

 i <<= x;  // i *= 2^x;
 i >>= y;  // i /= 2^y;

1
0x7e0 giống với 11111100000 là số thập phân 2016.
Saheed

50

Mặt nạ bit & dịch chuyển

Dịch chuyển bit thường được sử dụng trong lập trình đồ họa cấp thấp. Ví dụ: một giá trị màu pixel đã cho được mã hóa bằng một từ 32 bit.

 Pixel-Color Value in Hex:    B9B9B900
 Pixel-Color Value in Binary: 10111001  10111001  10111001  00000000

Để hiểu rõ hơn, cùng một giá trị nhị phân được dán nhãn với phần nào đại diện cho phần màu nào.

                                 Red     Green     Blue       Alpha
 Pixel-Color Value in Binary: 10111001  10111001  10111001  00000000

Ví dụ, giả sử chúng ta muốn lấy giá trị xanh của màu của pixel này. Chúng ta có thể dễ dàng có được giá trị đó bằng cách che dấuthay đổi .

Mặt nạ của chúng tôi:

                  Red      Green      Blue      Alpha
 color :        10111001  10111001  10111001  00000000
 green_mask  :  00000000  11111111  00000000  00000000

 masked_color = color & green_mask

 masked_color:  00000000  10111001  00000000  00000000

&Toán tử logic đảm bảo rằng chỉ các giá trị trong đó mặt nạ là 1 được giữ. Điều cuối cùng bây giờ chúng ta phải làm là lấy giá trị nguyên chính xác bằng cách dịch chuyển tất cả các bit đó sang bên phải 16 vị trí (dịch chuyển phải logic) .

 green_value = masked_color >>> 16

Et voilà, chúng ta có số nguyên biểu thị lượng màu xanh lá cây trong màu của pixel:

 Pixels-Green Value in Hex:     000000B9
 Pixels-Green Value in Binary:  00000000 00000000 00000000 10111001
 Pixels-Green Value in Decimal: 185

Điều này thường được sử dụng để mã hóa hoặc giải mã các định dạng hình ảnh như jpg, pngvv


Không dễ dàng hơn để truyền bản gốc của bạn, giả sử cl_uint 32 bit, giống như cl_uchar4 và truy cập byte bạn muốn trực tiếp dưới dạng * .s2?
David H Parry

27

Một điều đáng chú ý là sau đây phụ thuộc vào việc triển khai (theo tiêu chuẩn ANSI):

char x = -1;
x >> 1;

x bây giờ có thể là 127 (01111111) hoặc vẫn -1 (11111111).

Trong thực tế, nó thường là cái sau.


4
Nếu tôi nhớ lại một cách chính xác, tiêu chuẩn ANSI C nói rõ ràng điều này phụ thuộc vào việc triển khai, vì vậy bạn cần kiểm tra tài liệu của nhà biên dịch để xem cách nó được triển khai nếu bạn muốn dịch chuyển số nguyên có chữ ký trên mã của mình.
Joe Pineda

Có, tôi chỉ muốn nhấn mạnh tiêu chuẩn ANSI đã nói như vậy, đó không phải là trường hợp các nhà cung cấp đơn giản là không tuân theo tiêu chuẩn hoặc tiêu chuẩn không nói gì về trường hợp particualr này.
Joe Pineda

22

Tôi chỉ viết mẹo và thủ thuật. Nó có thể hữu ích trong các bài kiểm tra và kỳ thi.

  1. n = n*2: n = n<<1
  2. n = n/2: n = n>>1
  3. Kiểm tra xem n có phải là sức mạnh của 2 (1,2,4,8, ...) không: kiểm tra !(n & (n-1))
  4. Lấy bit thứ x của :nn |= (1 << x)
  5. Kiểm tra xem x chẵn hay lẻ: x&1 == 0(chẵn)
  6. Chuyển đổi bit thứ n của x:x ^ (1<<n)

Có phải có thêm một vài mà bạn biết bây giờ?
ryyker

@ryyker Mình có thêm vài cái nữa. Tôi sẽ cố gắng tiếp tục cập nhật nó :)
Ravi Prakash

Là x và n 0 được lập chỉ mục?
reggaeg Ức

Quảng cáo 5.: Nếu đó là một số âm thì sao?
Peter Mortensen

vì vậy, chúng ta có thể kết luận 2 trong nhị phân giống như 10 trong số thập phân không? và dịch chuyển bit giống như thêm hoặc trừ một số nữa sau một số khác trong số thập phân?
Willy satrio nugroho

8

Lưu ý rằng trong quá trình triển khai Java, số lượng bit cần dịch chuyển được sửa đổi theo kích thước của nguồn.

Ví dụ:

(long) 4 >> 65

bằng 2. Bạn có thể mong đợi dịch chuyển các bit sang bên phải 65 lần sẽ loại bỏ mọi thứ, nhưng thực tế nó tương đương với:

(long) 4 >> (65 % 64)

Điều này đúng với <<, >> và >>>. Tôi đã không thử nó trong các ngôn ngữ khác.


Hừ, thú vị! Trong C, đây là hành vi không xác định về mặt kỹ thuật . gcc 5.4.0đưa ra cảnh báo, nhưng đưa ra 2cho 5 >> 65; cũng.
pizzapants184

2

Một số thao tác / thao tác bit hữu ích trong Python.

Tôi đã triển khai câu trả lời của Ravi Prakash bằng Python.

# Basic bit operations
# Integer to binary
print(bin(10))

# Binary to integer
print(int('1010', 2))

# Multiplying x with 2 .... x**2 == x << 1
print(200 << 1)

# Dividing x with 2 .... x/2 == x >> 1
print(200 >> 1)

# Modulo x with 2 .... x % 2 == x & 1
if 20 & 1 == 0:
    print("20 is a even number")

# Check if n is power of 2: check !(n & (n-1))
print(not(33 & (33-1)))

# Getting xth bit of n: (n >> x) & 1
print((10 >> 2) & 1) # Bin of 10 == 1010 and second bit is 0

# Toggle nth bit of x : x^(1 << n)
# take bin(10) == 1010 and toggling second bit in bin(10) we get 1110 === bin(14)
print(10^(1 << 2))

-3

Xin lưu ý rằng chỉ có phiên bản 32 bit của PHP trên nền tảng Windows.

Sau đó, nếu bạn thay đổi << hoặc >> nhiều hơn 31 bit, kết quả sẽ không thể mở rộng. Thông thường số gốc thay vì số không sẽ được trả về, và nó có thể là một lỗi thực sự khó khăn.

Tất nhiên, nếu bạn sử dụng phiên bản 64 bit của PHP (Unix), bạn nên tránh dịch chuyển hơn 63 bit. Tuy nhiên, ví dụ, MySQL sử dụng BIGINT 64 bit, do đó không nên có bất kỳ vấn đề tương thích nào.

CẬP NHẬT: Từ PHP 7 Windows, các bản dựng PHP cuối cùng cũng có thể sử dụng toàn bộ số nguyên 64 bit: Kích thước của một số nguyên phụ thuộc vào nền tảng, mặc dù giá trị tối đa khoảng hai tỷ là giá trị thông thường (đó là 32 bit được ký). Các nền tảng 64 bit thường có giá trị tối đa khoảng 9E18, ngoại trừ trên Windows trước PHP 7, trong đó luôn là 32 bit.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.