Tại sao không có hướng dẫn `nand` trong các CPU hiện đại?


52

Tại sao các nhà thiết kế x86 (hoặc các kiến ​​trúc CPU khác) quyết định không bao gồm nó? Nó là một cổng logic có thể được sử dụng để xây dựng các cổng logic khác, do đó nó nhanh như một lệnh đơn. Thay vì xâu chuỗi notandhướng dẫn (cả hai đều được tạo từ nand), tại sao không có nandhướng dẫn?.


20
Usecase nào bạn có cho hướng dẫn nand? Có lẽ các nhà thiết kế x86 không bao giờ tìm thấy bất kỳ
PlasmaHH

16
ARM có BIChướng dẫn, đó là a & ~b. Arm Thumb-2 có ORNhướng dẫn đó là ~(a | b). ARM khá hiện đại. Mã hóa một lệnh trong tập lệnh CPU có chi phí của nó. Vì vậy, chỉ những người "hữu ích" nhất mới tìm được đường vào ISA.
Eugene Sh.

24
@Amumu Chúng tôi cũng có thể có ~(((a << 1) | (b >> 1)) | 0x55555555)hướng dẫn. Mục đích sẽ là để ~(((a << 1) | (b >> 1)) | 0x55555555)có thể được dịch thành một hướng dẫn duy nhất thay vì 6. Vậy, tại sao không?
dùng253751

11
@Amumu: Đó không phải là một usecase, và cũng là ~ không! Một usecase là một lý do thuyết phục tại sao hướng dẫn đó hữu ích và nơi nó có thể được áp dụng. Lý luận của bạn giống như nói "Hướng dẫn nên có để có thể sử dụng nó" nhưng câu hỏi đặt ra là "sử dụng nó cho điều gì quan trọng đến mức hữu ích để tiêu tốn tài nguyên".
PlasmaHH

4
Tôi đã lập trình được 45 năm, đã viết một vài trình biên dịch và sử dụng một số toán tử logic mạnh hơn khi có sẵn như IMP, nhưng tôi chưa bao giờ sử dụng cho toán tử hoặc lệnh NAND.
dùng207421

Câu trả lời:


62

http://www.ibm.com/support/ledgeledgecenter/ssw_aix_61/com.ibm.aix.alangref/idalangref_nand_nd_instrs.htmlm : POWER có NAND.

Nhưng nhìn chung các CPU hiện đại được xây dựng để phù hợp với việc tạo mã tự động bởi các trình biên dịch và NAND bitwise rất hiếm khi được yêu cầu. Bitwise AND và OR được sử dụng thường xuyên hơn để thao tác các bitfield trong cấu trúc dữ liệu. Trên thực tế, SSE có AND-NOT nhưng không phải NAND.

Mỗi hướng dẫn đều có chi phí trong logic giải mã và tiêu thụ một opcode có thể được sử dụng cho mục đích khác. Đặc biệt là trong các mã hóa có độ dài thay đổi như x86, bạn có thể hết các mã ngắn và phải sử dụng các mã dài hơn, có khả năng làm chậm tất cả mã.


5
@supercat AND-NOT thường được sử dụng để tắt bit trong biến bit-set. ví dụif(windowType & ~WINDOW_RESIZABLE) { ... do stuff for variable-sized windows ... }
Adib

2
@adib: Yup. Một tính năng thú vị của "và không" là không giống như toán tử "bitwise not" [~] kích thước kết quả sẽ không thành vấn đề. Nếu foolà một uint64_t, câu lệnh foo &= ~something;đôi khi có thể xóa nhiều bit hơn dự định, nhưng nếu có một &~=toán tử thì có thể tránh được các vấn đề như vậy.
supercat

6
@adib nếu WINDOW_RESIZABLElà hằng số, thì trình tối ưu hóa sẽ đánh giá ~WINDOW_RESIZABLEtại thời điểm biên dịch, vì vậy đây chỉ là AND trong thời gian chạy.
alephzero

4
@MarkRansom: Không, nguyên nhân và kết quả hoàn toàn chính xác từ lịch sử điện toán. Hiện tượng thiết kế CPU được tối ưu hóa cho trình biên dịch thay vì lập trình viên lắp ráp của con người là một phần của phong trào RISC (mặc dù, chính phong trào RISC rộng hơn so với khía cạnh đó). CPU được thiết kế cho trình biên dịch bao gồm ARM và Atmel AVR. Vào cuối những năm 90 và đầu những năm 00, người ta đã thuê các nhà văn biên dịch và lập trình viên hệ điều hành để thiết kế các bộ hướng dẫn CPU
slebetman

3
Ngày nay, hoạt động đăng ký để đăng ký về cơ bản là miễn phí so với truy cập RAM. Việc thực hiện các hướng dẫn dự phòng có chi phí bất động sản silicon trong CPU. Do đó, thường sẽ chỉ có một dạng bitwise-OR và bitwise-AND bởi vì việc thêm một hoạt động đăng ký thanh ghi bổ sung bitwise sẽ hầu như không làm chậm bất cứ điều gì.
nigel 222

31

Chi phí của các hàm ALU như vậy là

1) logic thực hiện chức năng chính nó

2) bộ chọn chọn kết quả chức năng này thay vì các chức năng khác trong số tất cả các chức năng ALU

3) chi phí để có tùy chọn này trong tập lệnh (và không có một số chức năng hữu ích khác)

Tôi đồng ý với bạn rằng chi phí 1) là rất nhỏ. Tuy nhiên, chi phí 2) và 3) gần như không phụ thuộc vào chức năng. Tôi nghĩ trong trường hợp này, chi phí 3) (các bit chiếm trong hướng dẫn) là lý do không có hướng dẫn cụ thể này. Các bit trong một lệnh là một nguồn tài nguyên rất khan hiếm cho một nhà thiết kế CPU / kiến ​​trúc.


29

Xoay quanh nó - đầu tiên hãy xem tại sao Nand lại phổ biến trong thiết kế logic phần cứng - nó có một số thuộc tính hữu ích ở đó. Sau đó hỏi xem các thuộc tính đó có còn áp dụng trong lệnh CPU không ...

TL / DR - họ không, vì vậy không có nhược điểm nào khi sử dụng And, Hoặc hoặc Không thay thế.

Lợi thế lớn nhất đối với logic Nand được tăng cường là tốc độ, đạt được bằng cách giảm số lượng mức logic (giai đoạn bóng bán dẫn) giữa đầu vào và đầu ra của mạch. Trong CPU, tốc độ xung nhịp được xác định bằng tốc độ của các hoạt động phức tạp hơn nhiều như bổ sung, do đó, việc tăng tốc độ hoạt động AND sẽ không cho phép bạn tăng tốc độ xung nhịp.

Và số lần bạn cần kết hợp các hướng dẫn khác rất ít - đủ để Nand thực sự không kiếm được khoảng trống trong tập hướng dẫn.


1
Trong trường hợp không yêu cầu cách ly đầu vào, "và không" có vẻ rất rẻ trong phần cứng. Quay trở lại năm 1977, tôi đã thiết kế bộ điều khiển tín hiệu rẽ cho đoạn giới thiệu của bố mẹ tôi bằng cách sử dụng hai bóng bán dẫn và hai điốt mỗi đèn để thực hiện chức năng "XOR" [đèn trái == xor (tín hiệu trái, phanh); đèn bên phải == xor (tín hiệu bên phải, phanh)], về cơ bản là hai chức năng nối dây hoặc không phải cho mỗi đèn. Tôi chưa thấy các thủ thuật như vậy được sử dụng trong thiết kế LSI, nhưng tôi sẽ nghĩ rằng trong TTL hoặc NMOS, trong trường hợp bất cứ việc gì cho đầu vào đều có khả năng ổ đĩa đầy đủ, các thủ thuật đó có thể tiết kiệm được mạch điện.
supercat

12

Tôi muốn đồng ý với Brian ở đây, và Wouter và pjc50.

Tôi cũng muốn thêm rằng trên mục đích chung, đặc biệt là CISC, các bộ xử lý, các hướng dẫn đều không có cùng thông lượng - một thao tác phức tạp có thể chỉ cần thực hiện nhiều chu kỳ dễ dàng hơn.

Hãy xem xét X86: AND(đó là một hoạt động "và") có thể rất nhanh. Cùng đi cho NOT. Hãy xem xét một chút về sự tháo gỡ:

Mã đầu vào:

#include <immintrin.h>
#include <stdint.h>

__m512i nand512(__m512i a, __m512i b){return ~(a&b);}
__m256i nand256(__m256i a, __m256i b){return ~(a&b);}
__m128i nand128(__m128i a, __m128i b){return ~(a&b);}
uint64_t nand64(uint64_t a, uint64_t b){return ~(a&b);}
uint32_t nand32(uint32_t a, uint32_t b){return ~(a&b);}
uint16_t nand16(uint16_t a, uint16_t b){return ~(a&b);}
uint8_t nand8(uint8_t a, uint8_t b){return ~(a&b);}

Lệnh sản xuất lắp ráp:

gcc -O3 -c -S  -mavx512f test.c

Đầu ra hội (rút ngắn):

    .file   "test.c"
nand512:
.LFB4591:
    .cfi_startproc
    vpandq  %zmm1, %zmm0, %zmm0
    vpternlogd  $0xFF, %zmm1, %zmm1, %zmm1
    vpxorq  %zmm1, %zmm0, %zmm0
    ret
    .cfi_endproc
nand256:
.LFB4592:
    .cfi_startproc
    vpand   %ymm1, %ymm0, %ymm0
    vpcmpeqd    %ymm1, %ymm1, %ymm1
    vpxor   %ymm1, %ymm0, %ymm0
    ret
    .cfi_endproc
nand128:
.LFB4593:
    .cfi_startproc
    vpand   %xmm1, %xmm0, %xmm0
    vpcmpeqd    %xmm1, %xmm1, %xmm1
    vpxor   %xmm1, %xmm0, %xmm0
    ret
    .cfi_endproc
nand64:
.LFB4594:
    .cfi_startproc
    movq    %rdi, %rax
    andq    %rsi, %rax
    notq    %rax
    ret
    .cfi_endproc
nand32:
.LFB4595:
    .cfi_startproc
    movl    %edi, %eax
    andl    %esi, %eax
    notl    %eax
    ret
    .cfi_endproc
nand16:
.LFB4596:
    .cfi_startproc
    andl    %esi, %edi
    movl    %edi, %eax
    notl    %eax
    ret
    .cfi_endproc
nand8:
.LFB4597:
    .cfi_startproc
    andl    %esi, %edi
    movl    %edi, %eax
    notl    %eax
    ret
    .cfi_endproc

Như bạn có thể thấy, đối với các loại dữ liệu có kích thước dưới 64, mọi thứ chỉ đơn giản được xử lý dưới dạng dài (do đó và l và không phải l ), vì đó có vẻ là băng thông "gốc" của trình biên dịch của tôi.

Việc có movmột ở giữa chỉ là do thực tế eaxlà thanh ghi chứa giá trị trả về của hàm. Thông thường, bạn chỉ cần tính toán trong ediđăng ký mục đích chung để tính toán với kết quả.

Đối với 64 bit, nó giống nhau - chỉ với các từ "quad" (do đó, trailing q) và rax/ rsithay vì eax/ edi.

Có vẻ như đối với các toán hạng 128 bit và lớn hơn, Intel đã không quan tâm đến việc thực hiện thao tác "không"; thay vào đó, trình biên dịch tạo ra một 1thanh ghi tất cả (tự so sánh thanh ghi với chính nó, kết quả được lưu trong thanh ghi với vdcmpeqdlệnh) và xors đó.

Tóm lại: Bằng cách triển khai một thao tác phức tạp với nhiều hướng dẫn cơ bản, bạn không nhất thiết phải làm chậm hoạt động - đơn giản là không có lợi thế nào khi có một lệnh thực hiện nhiều lệnh nếu không nhanh hơn.


10

Trước hết đừng nhầm lẫn các hoạt động logic và bit.

Các thao tác bitwise thường được sử dụng để thiết lập / xóa / chuyển / kiểm tra bit trong bitfield. Không có thao tác nào trong số này yêu cầu nand ("và không", còn được gọi là "bit Clear" hữu ích hơn).

Các hoạt động logic trong hầu hết các ngôn ngữ lập trình hiện đại được đánh giá bằng logic ngắn mạch. Vì vậy, thường là một cách tiếp cận dựa trên chi nhánh để thực hiện chúng là cần thiết. Ngay cả khi trình biên dịch có thể xác định rằng đánh giá ngắn mạch và hoàn thành không có sự khác biệt đối với hành vi chương trình, các toán hạng cho các hoạt động logic thường không ở dạng thuận tiện để thực hiện biểu thức bằng cách sử dụng các hoạt động asm bitwise.


10

NAND thường không được triển khai trực tiếp vì có lệnh AND ngầm cho bạn khả năng nhảy theo điều kiện NAND.

Việc thực hiện một hoạt động logic trong CPU thường đặt các bit trong thanh ghi cờ.

Hầu hết các thanh ghi cờ có cờ ZERO. Cờ zero được đặt nếu kết quả của phép toán logic bằng 0 và bị xóa khác.

Hầu hết các CPU hiện đại đều có lệnh nhảy nhảy nếu cờ không được đặt. Họ cũng có một cấu trúc nhảy nếu cờ không được đặt.

VÀ và NAND là bổ sung. Nếu kết quả của phép toán AND bằng 0 thì kết quả của phép toán NAND là 1 và ngược lại.

Vì vậy, nếu bạn muốn nhảy ot nếu NAND của hai giá trị là đúng thì chỉ cần thực hiện thao tác AND và nhảy nếu cờ zero được đặt.

Vì vậy, nếu bạn muốn nhảy ot nếu NAND của hai giá trị là sai thì chỉ cần thực hiện thao tác AND và nhảy nếu cờ zero rõ ràng.


Thật vậy - sự lựa chọn của lệnh nhảy có điều kiện cho bạn lựa chọn logic đảo ngược và không đảo ngược cho cả lớp hoạt động, mà không phải thực hiện lựa chọn đó cho từng cá nhân.
Chris Stratton

Đây phải là câu trả lời tốt nhất. Các hoạt động cờ không làm cho NAND trở nên thừa đối với các hoạt động logic vì AND + JNZ và AND + JZ về cơ bản là ngắn mạch / logic AND và NAND tương ứng, cả hai đều có cùng số opcode.
Lie Ryan

4

Chỉ vì thứ gì đó rẻ không có nghĩa là nó hiệu quả .

Nếu chúng tôi lấy lý do quảng cáo tranh luận của bạn, chúng tôi sẽ đưa ra kết luận rằng CPU nên được cấu thành chủ yếu từ hàng trăm hương vị của hướng dẫn NOP - bởi vì chúng rẻ nhất để thực hiện.

Hoặc so sánh nó với các công cụ tài chính: bạn sẽ mua trái phiếu $ 1 với lợi nhuận 0,01% chỉ vì bạn có thể? Không, bạn muốn tiết kiệm những đô la đó cho đến khi bạn có đủ để mua một trái phiếu $ 10 với lợi nhuận tốt hơn. Điều tương tự cũng xảy ra với ngân sách silicon trên CPU: hiệu quả trong việc sử dụng nhiều ops rẻ tiền nhưng vô dụng như NAND và đặt các bóng bán dẫn đã lưu vào một thứ đắt tiền hơn nhưng thực sự hữu ích.

Không có cuộc đua để có càng nhiều op càng tốt. Như RISC vs CISC đã chứng minh những gì Turing biết ngay từ đầu: ít hơn là nhiều hơn. Thực sự tốt hơn là có càng ít op càng tốt.


nopkhông thể thực hiện tất cả các cổng logic khác, nhưng nandhoặc norcó thể, tái tạo hiệu quả bất kỳ lệnh nào được triển khai trong CPU trong phần mềm. Nếu chúng ta thực hiện phương pháp RISC, đó là ..
Amumu

@Amumu Tôi nghĩ bạn đang trộn lẫn gateinstruction. Gates được sử dụng để thực hiện các hướng dẫn, không phải là cách khác. NOPlà một chỉ dẫn, không phải là một cổng. Và đúng vậy, CPU chứa hàng ngàn hoặc thậm chí hàng triệu cổng NAND để thực hiện tất cả các hướng dẫn. Không phải là hướng dẫn "NAND".
Đặc vụ_L

2
@Amumu Đó không phải là cách tiếp cận RISC :) Đó là cách tiếp cận "sử dụng trừu tượng rộng nhất", không quá hữu ích bên ngoài các ứng dụng rất cụ thể. Chắc chắn, nandlà một cổng có thể được sử dụng để thực hiện các cổng khác; nhưng bạn đã có tất cả các hướng dẫn khác . Thực hiện lại chúng bằng cách sử dụng một nandhướng dẫn sẽ chậm hơn . Và chúng được sử dụng quá thường xuyên để chịu đựng điều đó, không giống như ví dụ cụ thể được chọn bằng cherry của bạn, nơi nandsẽ tạo ra mã ngắn hơn (không phải mã nhanh hơn , chỉ ngắn hơn); nhưng điều đó cực kỳ hiếm, và lợi ích đơn giản là không đáng giá.
Luaan

@Amumu Nếu chúng tôi sử dụng phương pháp của bạn, chúng tôi sẽ không có số vị trí. Điểm quan trọng khi bạn có thể nói đơn giản ((((()))))thay vì 5, phải không? Năm chỉ là một con số cụ thể, điều đó quá hạn chế - các bộ tổng quát hơn nhiều: P
Luaan

@Agent_L Có, tôi biết các cổng thực hiện hướng dẫn. nandthực hiện tất cả các cổng, do đó hoàn toàn nandcó thể thực hiện tất cả các hướng dẫn khác. Sau đó, nếu một lập trình viên có nandsẵn một hướng dẫn, anh ta có thể phát minh ra các hướng dẫn của riêng mình khi suy nghĩ trong các cổng logic. Điều tôi muốn nói ngay từ đầu là nếu nó rất cơ bản, tại sao nó không được đưa ra hướng dẫn riêng của nó (nghĩa là một mã opcode trong logic giải mã), vì vậy một lập trình viên có thể sử dụng hướng dẫn đó. Tất nhiên sau khi tôi được trả lời, bây giờ tôi biết nó phụ thuộc vào việc sử dụng phần mềm.
Amumu

3

Ở cấp độ phần cứng, nand hoặc cũng không phải là hoạt động logic cơ bản. Tùy thuộc vào công nghệ (hoặc tùy thuộc vào những gì bạn tùy ý gọi 1 và những gì bạn gọi là 0), có thể được thực hiện hoặc không theo cách đơn giản, cơ bản.

Nếu chúng ta bỏ qua trường hợp "cũng không", tất cả các logic khác được xây dựng từ nand. Nhưng không phải vì có một số bằng chứng khoa học máy tính cho thấy tất cả các hoạt động logic có thể được xây dựng từ và - lý do là không có phương pháp cơ bản nào để xây dựng xor, hoặc, v.v. tốt hơn là xây dựng nó từ nand.

Đối với hướng dẫn máy tính, tình hình là khác nhau. Một hướng dẫn nand có thể được thực hiện, và nó sẽ rẻ hơn một chút so với thực hiện xor, ví dụ. Nhưng chỉ một chút xíu, bởi vì logic tính toán kết quả rất nhỏ so với logic giải mã lệnh, di chuyển toán hạng xung quanh, đảm bảo rằng một thao tác chỉ được tính toán và chọn kết quả và đưa nó đến đúng nơi. Mỗi lệnh mất một chu kỳ để thực hiện, giống như một phép cộng phức tạp hơn gấp mười lần về mặt logic. Tiết kiệm của nand so với xor sẽ không đáng kể.

Điều quan trọng sau đó là có bao nhiêu hướng dẫn cần thiết cho các hoạt động được thực hiện bởi mã thông thường . Nand không ở đâu gần đầu danh sách các hoạt động thường được yêu cầu. Nó phổ biến hơn nhiều và, hoặc, không được yêu cầu. Các nhà thiết kế bộ xử lý và bộ hướng dẫn sẽ kiểm tra rất nhiều mã hiện có và xác định các hướng dẫn khác nhau sẽ ảnh hưởng đến mã đó như thế nào. Họ rất có thể nhận thấy rằng việc thêm một lệnh nand sẽ giúp giảm rất ít số lượng lệnh của bộ xử lý thực thi để chạy mã thông thường và thay thế một số lệnh hiện có bằng nand sẽ làm tăng số lượng lệnh được thực hiện.


2

Chỉ vì NAND (hoặc NOR) có thể thực hiện tất cả các cổng theo logic tổ hợp, không dịch sang toán tử bitwise hiệu quả theo cùng một cách. Để thực hiện AND chỉ sử dụng các thao tác NAND, trong đó c = a AND b, bạn phải có c = a NAND b, sau đó b = -1, sau đó c = c NAND b (đối với KHÔNG). Các hoạt động logic bitwise cơ bản là AND, OR, EOR, NOT, NAND và NEOR. Đó không phải là nhiều để trang trải, và bốn cái đầu tiên thường được xây dựng trong dù sao. Trong logic tổ hợp, các mạch logic cơ bản chỉ bị giới hạn bởi số lượng cổng có sẵn, đây là một trò chơi bóng khác nhau hoàn toàn. Số lượng kết nối có thể có trong một mảng cổng có thể lập trình, nghe có vẻ giống như những gì bạn thực sự theo sau, sẽ là một con số rất lớn thực sự. Một số bộ xử lý thực sự có các mảng cổng được xây dựng trong.


0

Bạn không triển khai một cổng logic chỉ vì nó có đầy đủ chức năng, đặc biệt là nếu các cổng logic khác thực sự có sẵn. Bạn thực hiện những gì có xu hướng được sử dụng nhiều nhất bởi trình biên dịch.

NAND, NOR và XNOR rất hiếm khi cần thiết. Bên cạnh các toán tử bitwise cổ điển AND, OR và XOR, chỉ ANDN ( ~a & b) - không phải là NAND ( ~(a & b)) - sẽ có một tiện ích thực tế. Nếu có, CPU nên thực hiện điều đó (và thực tế một số CPU thực hiện ANDN).

Để giải thích tiện ích thực tế của ANDN, hãy tưởng tượng bạn có một bitmask sử dụng nhiều bit, nhưng bạn chỉ quan tâm đến một số trong số đó, như sau:

enum my_flags {
    IT_IS_FRIDAY = 1,
    ...
    IT_IS_WARM = 8,
    ...
    THE_SUN_SHINES = 64,
    ...
};

Thông thường bạn muốn kiểm tra các bit quan tâm của mình trong bitmask xem

  1. Tất cả đều được thiết lập
  2. Ít nhất một được thiết lập
  3. Ít nhất một cái không được đặt
  4. Không có gì được thiết lập

Hãy bắt đầu bằng cách thu thập các bit quan tâm của bạn:

#define BITS_OF_INTEREST (IT_IS_FRIDAY | IT_IS_WARM | THE_SUN_SHINES)

1. Tất cả các bit quan tâm được đặt: bitwise ANDN + logic KHÔNG

Hãy nói rằng bạn muốn biết nếu tất cả các bit quan tâm của bạn được thiết lập. Bạn có thể thấy nó như là (my_bitmask & IT_IS_FRIDAY) && (my_bitmask & IT_IS_WARM) && (my_bitmask & THE_SUN_SHINES). Tuy nhiên, thông thường bạn sẽ thu gọn nó vào

unsigned int life_is_beautiful = !(~my_bitmask & BITS_OF_INTEREST);

2. Ít nhất một bit quan tâm được đặt: bitwise AND

Bây giờ hãy nói rằng bạn muốn biết nếu ít nhất một chút quan tâm được thiết lập. Bạn có thể thấy nó như (my_bitmask & IT_IS_FRIDAY) || (my_bitmask & IT_IS_WARM) || (my_bitmask & THE_SUN_SHINES). Tuy nhiên, thông thường bạn sẽ thu gọn nó vào

unsigned int life_is_not_bad = my_bitmask & BITS_OF_INTEREST;

3. Ít nhất một bit quan tâm không được đặt: bitwise ANDN

Bây giờ hãy nói rằng bạn muốn biết nếu ít nhất một chút quan tâm không được đặt. Bạn có thể thấy nó như !(my_bitmask & IT_IS_FRIDAY) || !(my_bitmask & IT_IS_WARM) || !(my_bitmask & THE_SUN_SHINES). Tuy nhiên, thông thường bạn sẽ thu gọn nó vào

unsigned int life_is_imperfect = ~my_bitmask & BITS_OF_INTEREST;

4. Không có chút quan tâm nào được đặt: bitwise AND + logic KHÔNG

Bây giờ hãy nói rằng bạn muốn biết nếu tất cả các bit quan tâm không được đặt. Bạn có thể thấy nó như !(my_bitmask & IT_IS_FRIDAY) && !(my_bitmask & IT_IS_WARM) && !(my_bitmask & THE_SUN_SHINES). Tuy nhiên, thông thường bạn sẽ thu gọn nó vào

unsigned int life_is_horrible = !(my_bitmask & BITS_OF_INTEREST);

Đây là các hoạt động phổ biến được thực hiện trên bitmask, cộng với bitwise OR và XOR cổ điển. Tôi nghĩ mặc dù một ngôn ngữ (không phải là CPU ) nên bao gồm các toán tử NAND, NOR và XNOR bitwise (có ký hiệu sẽ là ~&, ~|~^), mặc dù hiếm khi được sử dụng. Mặc dù vậy, tôi sẽ không bao gồm toán tử ANDN trong một ngôn ngữ, vì nó không giao hoán ( a ANDN bkhông giống như b ANDN a) - tốt hơn là viết ~a & bthay vì a ANDN b, trước đây cho thấy rõ hơn sự bất cân xứng của hoạt động.

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.