Tại sao số không dấu được thực hiện?


12

Tôi không thể hiểu tại sao các hệ thống vi xử lý thực hiện các số không dấu. Tôi đoán chi phí chỉ gấp đôi số nhánh có điều kiện, vì lớn hơn, ít hơn, .etc, cần một thuật toán khác với ký, vẫn có thuật toán nào cho thấy số không dấu là một lợi thế đáng kể?

Câu hỏi của tôi một phần là tại sao chúng cần phải nằm trong tập lệnh trái ngược với sự hỗ trợ của trình biên dịch?


27
Về cơ bản các số không dấu là tiêu chuẩn, đã ký được thực hiện để cung cấp số âm.
Pieter B

37
Rất nhiều dữ liệu của thế giới là số. Dữ liệu không phải là số dễ dàng được xử lý bằng cách sử dụng các loại không dấu. Việc Java không có các kiểu số không dấu là một lỗi, điều này gây ra rất nhiều lỗi trong những thứ phải thao tác với dữ liệu không phải là số (ví dụ: nén, v.v.).
Erik Eidt

6
@jtw Erik nói rằng không có thứ gọi là màu pixel âm hay ký tự âm. Vì vậy, sẽ thật lãng phí khi sử dụng các số nguyên đã ký cho điều đó, bạn sẽ bỏ một nửa không gian địa chỉ.
Martin Maat

26
Tôi không chắc chắn nếu tôi ở đây một mình, nhưng tôi thấy rất hiếm khi tôi cần số nguyên có chữ ký khi phát triển ứng dụng. Hầu như tất cả thời gian tôi cần là số tự nhiên (không dấu) (kích thước dương, thường) hoặc số dấu phẩy động có chữ ký. Ngoại lệ là những thứ như tiền tệ, nhưng những thứ đó rất hiếm; với tôi, số nguyên không dấu là tiêu chuẩn và số nguyên có chữ ký là ngoại lệ!
Thomas

11
Từ quan điểm của CPU, hầu hết các số đều không được ký. Một vài hướng dẫn có thể diễn giải các bit là đã ký (.eg số học-dịch chuyển sang phải), nhưng phần bù hai thực sự cho phép CPU coi các số nguyên đã ký là số nguyên không dấu, nghĩa là CPU không yêu cầu (hoặc rất ít) để hỗ trợ cả hai .
Bắp ngô

Câu trả lời:


39

Số chưa ký là một cách giải thích một chuỗi các bit. Đây cũng là cách hiểu đơn giản nhất và được sử dụng nhiều nhất trong CPU vì địa chỉ và mã op chỉ đơn giản là các bit. Địa chỉ bộ nhớ / ngăn xếp và số học là nền tảng của bộ vi xử lý, tốt, xử lý. Di chuyển lên kim tự tháp trừu tượng, một cách giải thích thường xuyên khác của các bit là ký tự (ASCII, Unicode, EBCDIC). Sau đó, có những cách hiểu khác như Điểm nổi của IEEE, RGBA cho đồ họa, v.v. Không có số nào trong số này là các số được ký đơn giản (IEEE FP không đơn giản và số học sử dụng các số này rất phức tạp).

Ngoài ra, với số học không dấu, việc triển khai các cách khác là khá đơn giản (nếu không hiệu quả nhất). Chuyện này là không đúng sự thật.


3
EBCDIC chỉ có một "tôi".
Ruslan

4
@Ruslan - nhưng nó được phát âm giống như nó có hai. <g>
Pete Becker

5
@PeteBecker không, không. EBCDIC được phát âm là eb -see-tinh ranh.
Mike Nakis

19

Phần lớn chi phí phần cứng cho các hoạt động so sánh là phép trừ. Đầu ra của phép trừ được sử dụng bằng cách so sánh về cơ bản là ba bit trạng thái:

  • liệu tất cả các bit có bằng không (tức là điều kiện bằng nhau),
  • bit dấu của kết quả
  • bit mang của phép trừ (tức là bit thứ tự cao thứ 33 trên máy tính 32 bit)

Với sự kết hợp đúng đắn của việc kiểm tra ba bit này sau hoạt động trừ, chúng ta có thể xác định tất cả các hoạt động quan hệ đã ký, cũng như tất cả các hoạt động quan hệ không dấu (các bit này cũng là cách phát hiện tràn, ký so với không dấu). Phần cứng ALU cơ bản tương tự có thể được chia sẻ để thực hiện tất cả các so sánh này (không đề cập đến hướng dẫn trừ), cho đến khi kiểm tra lần cuối ba bit trạng thái đó, khác nhau theo so sánh quan hệ mong muốn. Vì vậy, nó không phải là rất nhiều phần cứng bổ sung.

Chi phí thực tế duy nhất là nhu cầu mã hóa các chế độ so sánh bổ sung trong kiến ​​trúc tập lệnh, có thể làm giảm nhẹ mật độ lệnh. Tuy nhiên, điều khá bình thường là phần cứng có rất nhiều hướng dẫn không được sử dụng bởi bất kỳ ngôn ngữ nào.


1
So sánh các số không dấu không yêu cầu phép trừ. nó có thể đạt được bằng cách so sánh từ trái sang phải theo bit.
Jonathan Rosenne

10
@JonathanRosenne Nhưng đó không phải là cách các bộ xử lý thực hiện nó. Ngược lại, bộ xử lý bổ sung 2 gần như không thể tưởng tượng được là không thực hiện phép trừ (có hoặc không mang / mượn) trong ALU của nó. Ngay sau đó, người ta đã nghĩ đến một nhà thiết kế là sử dụng ALU cần thiết này để giết một con chim khác bằng chính hòn đá đó. So sánh sau đó chỉ đơn giản là một phép trừ trong đó kết quả không được ghi lại vào tệp đăng ký.
Idillotexist Idillotexist

4
+1: đây là câu trả lời đúng cho câu hỏi được hỏi. Tóm tắt: bởi vì việc triển khai các hoạt động chưa được ký gần như miễn phí khi bạn đã thực hiện đã ký .
Periata Breatta

10
@PeriataBreatta Nó cũng hoạt động theo cách khác. Các số được ký và không dấu trong các CPU hiện đại gần như giống hệt nhau, đó là điểm chính mà OP không nhận ra. Ngay cả các hướng dẫn so sánh cũng giống nhau đối với chữ ký và không dấu - đó là một trong những lý do tại sao phần bù của hai phần thắng trong các cuộc chiến số nguyên đã ký :)
Luaan

3
@svidgen> như những câu trả lời khác đã nói, nó hoạt động theo cách khác. Mối quan tâm chính là số không dấu, được sử dụng cho tất cả mọi thứ về cơ bản (địa chỉ bộ nhớ, io / cổng, biểu diễn ký tự, khắc). Các số đã ký chỉ trở nên rẻ khi bạn đã ký và có ích trong trường hợp hiếm hoi mà chúng mong muốn.
quang phổ

14

Bởi vì, nếu bạn cần đếm thứ gì đó luôn luôn >= 0 , bạn sẽ không cần thiết phải cắt không gian đếm của mình xuống một nửa bằng cách sử dụng các số nguyên đã ký.

Hãy xem xét INT PK tăng tự động mà bạn có thể đang đặt trên các bảng cơ sở dữ liệu của mình. Nếu bạn sử dụng số nguyên đã ký ở đó, bảng của bạn sẽ lưu HALF càng nhiều bản ghi càng tốt cho cùng kích thước trường mà KHÔNG có lợi ích.

Hoặc các octet của một màu RGBa. Chúng tôi không muốn lúng túng bắt đầu đếm khái niệm số dương tự nhiên này ở một số âm. Một số đã ký sẽ phá vỡ mô hình tinh thần hoặc giảm một nửa không gian của chúng ta. Một số nguyên không dấu không chỉ phù hợp với khái niệm, mà còn cung cấp độ phân giải gấp đôi.

Từ quan điểm phần cứng, số nguyên không dấu là đơn giản. Chúng có lẽ là cấu trúc bit dễ nhất để thực hiện toán học. Và, không còn nghi ngờ gì nữa, chúng ta có thể đơn giản hóa phần cứng bằng cách mô phỏng các kiểu số nguyên (hoặc thậm chí là dấu phẩy động!) Trong một trình biên dịch. Vì vậy, tại sao cả số nguyên không dấu ký được thực hiện trong phần cứng?

Chà ... hiệu suất!

Hiệu quả hơn khi triển khai các số nguyên có chữ ký trong phần cứng so với phần mềm. Phần cứng có thể được hướng dẫn để thực hiện toán trên một trong hai loại số nguyên trong một lệnh. Và điều đó rất tốt , bởi vì phần cứng đập song song ít nhiều . Nếu bạn cố gắng mô phỏng điều đó trong phần mềm, loại số nguyên mà bạn chọn để "mô phỏng" chắc chắn sẽ yêu cầu nhiều hướng dẫn và chậm hơn đáng kể .


2
Dọc theo những dòng này, bạn có thể tiết kiệm cho mình một thao tác khi thực hiện kiểm tra giới hạn mảng. Nếu bạn sử dụng một số nguyên không dấu, bạn chỉ cần kiểm tra xem chỉ mục được cung cấp có nhỏ hơn kích thước mảng không (vì nó không thể âm).
riwalk

2
@ dan04 Điều đó chắc chắn có thể ... Nhưng, nếu bạn đang sử dụng int tăng tự động bắt đầu từ 0 hoặc 1, một cách khá phổ biến, bạn đã loại trừ việc sử dụng một nửa số có sẵn của mình. Và trong khi bạn có thể hình dung bắt đầu đếm ở -2 ^ 31 (hoặc bất cứ điều gì), bạn sẽ có một trường hợp "cạnh" tiềm năng ở giữa không gian id của bạn.
Svidgen

1
Cắt lĩnh vực của bạn một nửa là loại tranh luận yếu. Rất có thể nếu ứng dụng của bạn yêu cầu hơn 2 tỷ, nó cũng đòi hỏi hơn 4 tỷ.
corsiKa

1
@corsiKa: bởi lý do đó, nếu nó yêu cầu nhiều hơn 4, nó có thể yêu cầu 8, sau đó 16, v.v ... Nó kết thúc ở đâu?
tên gì là

1
@whatsisname nói chung, bạn sử dụng các loại số nguyên 8, 16, 32 hoặc 64 bit. Nói rằng không dấu là tốt hơn bởi vì bạn nhận được tất cả 32 bit thay vì phạm vi giới hạn 31 bit của không gian nguyên dương trong một byte đã ký là điều không quan trọng trong hầu hết các trường hợp.
corsiKa

9

Câu hỏi của bạn bao gồm hai phần:

  1. Mục đích của số nguyên không dấu là gì?

  2. Là số nguyên không dấu có giá trị rắc rối?

1. Mục đích của số nguyên không dấu là gì?

Các số không được gán, khá đơn giản, đại diện cho một lớp số lượng mà giá trị âm là vô nghĩa. Chắc chắn, bạn có thể nói rằng câu trả lời cho câu hỏi "tôi có bao nhiêu quả táo?" có thể là một số âm nếu bạn nợ một số táo với ai đó, nhưng còn câu hỏi về "tôi có bao nhiêu bộ nhớ?" - bạn không thể có một lượng bộ nhớ âm. Vì vậy, số nguyên không dấu rất phù hợp để đại diện cho số lượng như vậy và chúng có lợi ích là có thể đại diện gấp đôi phạm vi giá trị dương so với số nguyên đã ký có thể. Ví dụ: giá trị tối đa bạn có thể biểu thị với số nguyên có chữ ký 16 bit là 32767, trong khi với số nguyên không dấu 16 bit, nó là 65535.

2. Số nguyên không dấu có đáng để gặp rắc rối không?

Số nguyên không được ký không thực sự đại diện cho bất kỳ rắc rối nào, vì vậy, vâng, chúng đáng giá. Bạn thấy đấy, họ không yêu cầu thêm một "thuật toán"; mạch cần thiết để thực hiện chúng là một tập hợp con của mạch cần thiết để thực hiện các số nguyên đã ký.

Một CPU không có một số nhân cho các số nguyên đã ký và một số nhân khác nhau cho các số nguyên không dấu; nó chỉ có một số nhân, hoạt động theo một cách hơi khác tùy thuộc vào tính chất của hoạt động. Hỗ trợ phép nhân có chữ ký đòi hỏi một mạch nhỏ hơn một chút so với không dấu, nhưng vì dù sao nó cũng cần được hỗ trợ, phép nhân không dấu thực tế miễn phí, nên nó được bao gồm trong gói.

Đối với phép cộng và phép trừ, không có sự khác biệt nào trong mạch điện cả. Nếu bạn đọc về cái gọi là đại diện bổ sung của hai số nguyên, bạn sẽ thấy rằng nó được thiết kế rất thông minh để các thao tác này có thể được thực hiện theo cùng một cách, bất kể tính chất của các số nguyên.

So sánh cũng hoạt động theo cách tương tự, vì không có gì ngoài trừ và loại bỏ kết quả, sự khác biệt duy nhất là trong các lệnh (nhảy) nhánh có điều kiện, hoạt động bằng cách nhìn vào các cờ khác nhau của CPU được đặt bởi hướng dẫn (so sánh) trước. Trong câu trả lời này: /programming//a/9617990/773113 bạn có thể tìm thấy lời giải thích về cách chúng hoạt động trên kiến ​​trúc Intel x86. Điều gì xảy ra là việc chỉ định một lệnh nhảy có điều kiện là đã ký hoặc không dấu phụ thuộc vào cờ nào nó kiểm tra.


1
Câu hỏi của tôi giả định tất cả điều này, theo thuật toán tôi có nghĩa là quy tắc nhỏ hơn lớn hơn vv là khác nhau. Chi phí tôi thấy là có rất nhiều hướng dẫn thêm. Nếu các chương trình cấp cao muốn xem dữ liệu dưới dạng các mẫu của bit thì điều này có thể được thực hiện dễ dàng bằng trình biên dịch
jtw

3
@jtw - nhưng vấn đề là những hướng dẫn bổ sung đó thực sự rất giống với hướng dẫn cần thiết cho các số đã ký và hầu như tất cả các mạch cần thiết cho chúng đều có thể được chia sẻ . Chi phí bổ sung để thực hiện cả hai loại gần như bằng không.
Periata Breatta

1
vâng, câu trả lời cho câu hỏi của tôi, thêm các hướng dẫn chi nhánh bổ sung đi kèm với một chi phí nhỏ và chúng thường hữu ích trong thực tế
jtw

1
"Các hoạt động chưa được ký yêu cầu một số xử lý bổ sung khi phân chia và nhân" Tôi nghĩ rằng bạn có điều đó ngược lại. Nhân và chia dễ dàng hơn với các giá trị không dấu. Việc xử lý thêm là cần thiết để đối phó với các toán hạng đã ký.
Cody Grey

@CodyGray Tôi biết ai đó sẽ xuất hiện để nói điều này. Bạn đúng, tất nhiên. Đây là lý do đằng sau tuyên bố của tôi, mà ban đầu tôi đã bỏ qua vì lý do ngắn gọn: CPU không thể cung cấp phép nhân và chia chỉ không dấu, bởi vì các phiên bản đã ký rất hữu ích. Như một vấn đề của thực tế, nhân và ký đã ký là phải; không dấu là tùy chọn. Do đó, nếu không dấu cũng được cung cấp, điều này có thể được xem là đòi hỏi một mạch nhỏ hơn một chút.
Mike Nakis

7

Bộ vi xử lý vốn không được ký. Các số đã ký là thứ được thực hiện, không phải theo cách khác.

Máy tính có thể và hoạt động tốt mà không cần số đã ký, nhưng chính chúng ta, con người cần số âm, do đó, chữ ký đã được phát minh.


4
Nhiều bộ vi xử lý có cả hướng dẫn đã ký và chưa ký cho các hoạt động khác nhau.
tên của

1
@whatsisname: Điều ngược lại: nhiều bộ vi xử lý chỉ có các hướng dẫn không dấu. Một số đã ký hướng dẫn. Điều này là do với số học bổ sung 2s, giá trị bit là như nhau bất kể thời tiết, số được ký hay không dấu và cách người ta đọc số chỉ là vấn đề diễn giải - do đó việc triển khai nó như một tính năng của trình biên dịch sẽ dễ dàng hơn. Nói chung chỉ các micros cũ mà giả sử các lập trình viên không sử dụng trình biên dịch có các hướng dẫn được ký lạ mắt để làm cho mã lắp ráp có thể đọc được.
slebetman

3

Bởi vì chúng có thêm một bit có sẵn để lưu trữ và bạn không phải lo lắng về số âm. Không có nhiều hơn thế.

Bây giờ nếu bạn cần một ví dụ về nơi bạn sẽ cần thêm bit này, có rất nhiều thứ sẽ được tìm thấy nếu bạn nhìn.

Ví dụ yêu thích của tôi đến từ bitboard trong công cụ Chess. Có 64 ô vuông trên bàn cờ, do đó, unsigned longcung cấp lưu trữ hoàn hảo cho nhiều thuật toán xoay quanh việc tạo di chuyển. Xem xét thực tế rằng bạn cần hoạt động nhị phân (cũng như hoạt động theo ca !!), thật dễ dàng để biết lý do tại sao không phải lo lắng về những điều đặc biệt xảy ra nếu MSB được đặt. Nó có thể được thực hiện với chữ ký dài, nhưng sử dụng không dấu dễ dàng hơn nhiều .


3

Có một nền tảng toán học thuần túy, đây là một chút toán học hơn cho bất cứ ai quan tâm.

Nếu chúng ta bắt đầu với một số nguyên có dấu và không dấu 8 bit, về cơ bản chúng ta có các số nguyên modulo 256, cho đến khi có liên quan đến phép nhân và phép nhân, thì cung cấp phần bù 2 được sử dụng để biểu diễn các số nguyên âm (và đây là cách mà mọi bộ xử lý hiện đại thực hiện) .

Trường hợp mọi thứ khác nhau ở hai nơi: một là hoạt động so sánh. Theo một nghĩa nào đó, các số nguyên modulo 256 được coi là một vòng tròn số (giống như các số nguyên modulo 12 thực hiện trên mặt đồng hồ analog kiểu cũ). Để làm cho các so sánh số (là x <y) có ý nghĩa, chúng tôi cần phải quyết định số nào nhỏ hơn số khác. Từ quan điểm của nhà toán học, chúng tôi muốn nhúng số nguyên modulo 256 vào tập hợp tất cả các số nguyên bằng cách nào đó. Ánh xạ số nguyên 8 bit có biểu diễn nhị phân là tất cả các số 0 thành số nguyên 0 là điều hiển nhiên phải làm. Sau đó, chúng ta có thể tiến hành ánh xạ những cái khác sao cho '0 + 1' (kết quả của việc hủy đăng ký, giả sử ax và tăng nó lên một, thông qua 'inc ax') đi đến số nguyên 1, v.v. Chúng ta có thể làm tương tự với -1, ví dụ ánh xạ '0-1' sang số nguyên -1 và '0-1-1' đến số nguyên -2. Chúng tôi phải đảm bảo rằng phần nhúng này là một hàm, vì vậy không thể ánh xạ một số nguyên 8 bit đơn thành hai số nguyên. Như vậy, điều này có nghĩa là nếu chúng ta ánh xạ tất cả các số vào tập hợp số nguyên thì 0 sẽ ở đó, cùng với một số số nhỏ hơn 0 và một số hơn 0. Về cơ bản có 255 cách để thực hiện điều này với số nguyên 8 bit (theo đến mức tối thiểu bạn muốn, từ 0 đến -255). Sau đó, bạn có thể định nghĩa 'x <y' theo thuật ngữ '0 <y - x'.

Có hai trường hợp sử dụng phổ biến, trong đó hỗ trợ phần cứng là hợp lý: một trường hợp có tất cả các số nguyên khác không lớn hơn 0 và một trường hợp có khoảng cách chia 50/50 khoảng 0. Tất cả các khả năng khác có thể được mô phỏng dễ dàng bằng cách dịch các số thông qua thêm 'thêm và phụ 'trước khi hoạt động và nhu cầu này rất hiếm khi tôi không thể nghĩ ra một ví dụ rõ ràng trong phần mềm hiện đại (vì bạn chỉ có thể làm việc với một lớp phủ lớn hơn, giả sử là 16 bit).

Vấn đề khác là ánh xạ một số nguyên 8 bit vào không gian của các số nguyên 16 bit. -1 có đến -1 không? Đây là những gì bạn muốn nếu 0xFF có nghĩa là đại diện cho -1. Trong trường hợp này, mở rộng đăng nhập là điều hợp lý để làm, do đó 0xFF chuyển sang 0xFFFF. Mặt khác, nếu 0xFF có nghĩa là đại diện cho 255, thì bạn muốn nó được ánh xạ thành 255, do đó thành 0x00FF, thay vì 0xFFFF.

Đây cũng là điểm khác biệt giữa các hoạt động 'dịch chuyển' và 'dịch chuyển số học'.

Cuối cùng, tuy nhiên, thực tế là int trong phần mềm không phải là số nguyên, mà là các biểu diễn dưới dạng nhị phân và chỉ một số có thể được biểu diễn. Khi thiết kế phần cứng, các lựa chọn phải được thực hiện để thực hiện phần cứng. Vì với phần bù của 2, các phép toán cộng và nhân giống hệt nhau, nên thể hiện các số nguyên âm theo cách này. Sau đó, nó chỉ là vấn đề của các hoạt động phụ thuộc vào số nguyên mà biểu diễn nhị phân của bạn có nghĩa là đại diện.


Tôi thích cách tiếp cận toán học, nhưng thay vì chỉ nghĩ đến việc quảng bá đến một kích thước lớn hơn cụ thể, tôi nghĩ việc khái quát hóa các hoạt động trên một số nhị phân có độ dài vô hạn sẽ tốt hơn. Trừ 1 từ bất kỳ số nào có k chữ số ngoài cùng bên phải là 0 và k chữ số ngoài cùng bên phải của kết quả sẽ là 1 và người ta có thể chứng minh bằng cảm ứng rằng nếu một người thực hiện toán học với số bit vô hạn, thì mỗi bit sẽ là 1. Đối với dấu không dấu toán học, người ta bỏ qua tất cả trừ các bit dưới cùng của một số.
supercat

2

Cho phép kiểm tra chi phí thực hiện để thêm các số nguyên không dấu vào thiết kế CPU với các số nguyên đã ký hiện có.

Một CPU thông thường cần các hướng dẫn số học sau:

  • THÊM (thêm hai giá trị và đặt cờ nếu thao tác tràn)
  • SUB (trừ một giá trị từ một giá trị khác và đặt các cờ khác nhau - chúng ta sẽ thảo luận về các giá trị bên dưới)
  • CMP (về cơ bản là 'SUB và loại bỏ kết quả, chỉ giữ các cờ')
  • LSH (dịch chuyển trái, đặt cờ khi tràn)
  • RSH (dịch chuyển phải, đặt cờ nếu 1 được dịch ra)
  • Các biến thể của tất cả các hướng dẫn trên xử lý việc mang / mượn từ các cờ, do đó cho phép bạn xâu chuỗi các hướng dẫn lại với nhau một cách thuận tiện để hoạt động trên các loại lớn hơn các thanh ghi CPU
  • MUL (nhân, đặt cờ, v.v. - không phổ biến)
  • DIV (chia, đặt cờ, v.v. - rất nhiều kiến ​​trúc CPU thiếu điều này)
  • Chuyển từ loại số nguyên nhỏ hơn (ví dụ 16 bit) sang loại lớn hơn (ví dụ 32 bit). Đối với các số nguyên đã ký, điều này thường được gọi là MOVSX (di chuyển với dấu mở rộng).

Nó cũng cần hướng dẫn hợp lý:

  • Chi nhánh trên không
  • Chi nhánh lớn hơn
  • Chi nhánh ít
  • Chi nhánh tràn
  • Phiên bản phủ định của tất cả các bên trên

Để thực hiện các nhánh trên trên so sánh số nguyên đã ký, cách dễ nhất là đặt lệnh SUB đặt các cờ sau:

  • Số không. Đặt nếu phép trừ dẫn đến giá trị bằng không.
  • Tràn ra. Đặt nếu phép trừ mượn một giá trị từ bit có ý nghĩa nhất.
  • Ký tên. Đặt thành bit dấu của kết quả.

Sau đó, các nhánh số học được thực hiện như sau:

  • Nhánh trên 0: nếu cờ 0 được đặt
  • Nhánh trên ít hơn: nếu cờ ký hiệu khác với cờ tràn
  • Nhánh trên lớn hơn: nếu cờ dấu bằng với cờ tràn và cờ 0 thì rõ ràng.

Những tiêu cực của những điều này nên theo rõ ràng từ cách chúng được thực hiện.

Vì vậy, thiết kế hiện tại của bạn đã thực hiện tất cả những điều này cho các số nguyên đã ký. Bây giờ hãy xem xét những gì chúng ta cần làm để thêm số nguyên không dấu:

  • THÊM - việc thực hiện THÊM là giống hệt nhau.
  • SUB - chúng ta cần thêm một cờ phụ: cờ mang được đặt khi giá trị được mượn từ bên ngoài bit quan trọng nhất của người đăng ký.
  • CMP - không thay đổi
  • LSH - không thay đổi
  • RSH - sự dịch chuyển đúng cho các giá trị đã ký giữ lại giá trị của bit có ý nghĩa nhất. Đối với các giá trị không dấu, thay vào đó chúng ta nên đặt nó thành 0.
  • MUL - nếu kích thước đầu ra của bạn giống với đầu vào, không cần xử lý đặc biệt (x86 không có xử lý đặc biệt, nhưng chỉ vì nó có đầu ra thành một cặp đăng ký, nhưng lưu ý rằng cơ sở này thực sự khá hiếm khi được sử dụng, vì vậy sẽ rất một ứng cử viên rõ ràng hơn để rời khỏi bộ xử lý hơn các loại không dấu)
  • DIV - không cần thay đổi
  • Chuyển từ loại nhỏ hơn sang loại lớn hơn - cần thêm MOVZX, di chuyển với độ mở rộng bằng không. Lưu ý rằng MOVZX cực kỳ đơn giản để thực hiện.
  • Chi nhánh trên không - không thay đổi
  • Nhánh trên ít - nhảy khi mang cờ đặt.
  • Nhánh trên lớn hơn - nhảy nếu mang cờ và không rõ ràng.

Lưu ý rằng trong mỗi trường hợp, việc sửa đổi rất đơn giản và có thể được thực hiện đơn giản bằng cách bật hoặc tắt một phần nhỏ của mạch hoặc bằng cách thêm một thanh ghi cờ mới hơn có thể được kiểm soát bởi một giá trị cần được tính như một phần của hướng dẫn thực hiện nào.

Do đó, chi phí thêm các hướng dẫn không dấu là rất nhỏ . Về lý do tại sao nó nên được thực hiện , lưu ý rằng các địa chỉ bộ nhớ (và offset trong mảng) vốn là các giá trị không dấu. Vì các chương trình dành nhiều thời gian để thao tác các địa chỉ bộ nhớ, nên có một loại xử lý chúng chính xác sẽ giúp các chương trình dễ viết hơn.


cảm ơn bạn, câu trả lời này cho câu hỏi của tôi, chi phí nhỏ và hướng dẫn thường hữu ích
jtw

1
Phép nhân kích thước kép không được chỉ định là rất cần thiết khi thực hiện số học đa độ chính xác và có lẽ tốt cho việc cải thiện tốc độ tổng thể tốt hơn gấp 2 lần khi thực hiện mã hóa RSA. Ngoài ra, phân chia là khác nhau trong các trường hợp đã ký và không dấu, nhưng vì trường hợp không dấu dễ dàng hơn và phân chia đủ hiếm và đủ chậm để thêm một vài hướng dẫn sẽ không gây hại nhiều, điều đơn giản nhất là chỉ thực hiện phân chia không dấu và sau đó bọc nó bằng một số logic xử lý dấu hiệu.
supercat

2

Các số không dấu tồn tại phần lớn để xử lý các tình huống trong đó người ta cần một vòng đại số bao bọc (đối với loại không dấu 16 bit, nó sẽ là vòng của số nguyên đồng nhất mod 65536). Lấy một giá trị, thêm bất kỳ số tiền nào nhỏ hơn mô đun và chênh lệch giữa hai giá trị sẽ là số tiền được thêm vào. Như một ví dụ trong thế giới thực, nếu một đồng hồ tiện ích đọc 9995 vào đầu tháng và một người sử dụng 23 đơn vị, thì đồng hồ sẽ đọc 0018 vào cuối tháng. Khi sử dụng loại vòng đại số, không cần phải làm gì đặc biệt để xử lý tràn. Trừ 9995 từ 0018 sẽ mang lại 0023, chính xác là số lượng đơn vị đã được sử dụng.

Trên PDP-11, máy mà C được triển khai lần đầu tiên, không có loại số nguyên không dấu nhưng các loại đã ký có thể được sử dụng cho số học mô-đun bao gồm từ 32767 đến -32768 thay vì giữa 65535 và 0. Các hướng dẫn số nguyên trên một số khác nền tảng đã không bao bọc mọi thứ sạch sẽ, tuy nhiên; thay vì yêu cầu việc triển khai phải mô phỏng hai số nguyên bổ sung được sử dụng trong PDP-11, ngôn ngữ thay vào đó đã thêm các loại không dấu mà chủ yếu phải hành xử như các vòng đại số và cho phép các kiểu số nguyên được ký để hành xử theo cách khác trong trường hợp tràn.

Trong những ngày đầu của C, có nhiều đại lượng có thể vượt quá 32767 (INT_MAX chung) nhưng không phải là 65535 (UINT_MAX chung). Do đó, nó trở nên phổ biến để sử dụng các loại không dấu để giữ số lượng như vậy (ví dụ size_t). Thật không may, trong ngôn ngữ không có gì để phân biệt giữa các loại nên hành xử giống như các số có thêm một chút phạm vi dương, so với các loại nên hoạt động như các vòng đại số. Thay vào đó, ngôn ngữ làm cho các loại nhỏ hơn "int" hoạt động giống như các số trong khi các loại có kích thước đầy đủ hoạt động như các vòng đại số. Do đó, chức năng gọi như:

uint32_t mul(uint16_t a, uint16_t b) { return a*b; }

với (65535, 65535) sẽ có một hành vi được xác định trên các hệ thống có int16 bit (tức là trả về 1), một hành vi khác trong đó intcó 33 bit hoặc lớn hơn (trả về 0xFFFE0001) và Hành vi không xác định trên các hệ thống có "int" ở bất kỳ đâu trong- giữa [lưu ý rằng gcc thường sẽ mang lại kết quả chính xác với kết quả giữa INT_MAX + 1u và UINT_MAX, nhưng đôi khi sẽ tạo mã cho hàm trên không thành công với các giá trị đó!]. Không hữu ích lắm.

Tuy nhiên, việc thiếu các loại hoạt động nhất quán như số hoặc nhất quán như vòng đại số không làm thay đổi thực tế rằng các loại vòng đại số hầu như không thể thiếu đối với một số loại lập trình.

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.