Khai báo một số nguyên không dấu trong Java


316

Có cách nào để khai báo một số nguyên không dấu trong Java không?

Hoặc câu hỏi cũng có thể được đóng khung như thế này: Tương đương với Java không dấu là gì?

Chỉ để cho bạn biết bối cảnh mà tôi đang xem xét triển khai của Java String.hashcode(). Tôi muốn kiểm tra khả năng va chạm nếu số nguyên là 32 không dấu int.


7
Không có kiểu không dấu trong Java.
Andrew Logvinov

1
Bài đăng này có thể giúp bạn stackoverflow.com/a/4449161/778687
tusar

2
Dường như không phải là một cách AFAICT. Liên quan: stackoverflow.com/questions/430346/ từ
James Manning

11
Nó phụ thuộc vào mục đích bạn đang cố gắng để đạt được. Đối với hầu hết các mục đích, tất cả các số nguyên trong Java đều được ký. Tuy nhiên, bạn có thể coi một số nguyên đã ký là không dấu trong một trường hợp cụ thể: bạn có thể dịch chuyển sang phải mà không cần ký hiệu mở rộng bằng cách sử dụng >>>toán tử thay vì >>.
dasblinkenlight

Câu trả lời:


310

Java không có kiểu dữ liệu cho các số nguyên không dấu .

Bạn có thể xác định longthay vì intnếu bạn cần lưu trữ các giá trị lớn.

Bạn cũng có thể sử dụng một số nguyên đã ký như thể nó không được ký. Lợi ích của biểu diễn phần bù hai là hầu hết các phép toán (như phép cộng, phép trừ, phép nhân và dịch chuyển trái) giống hệt nhau ở cấp độ nhị phân cho các số nguyên có dấu và không dấu. Tuy nhiên, một vài thao tác (phân chia, dịch chuyển phải, so sánh và đúc) là khác nhau. Kể từ Java SE 8, các phương thức mới trong Integerlớp cho phép bạn sử dụng đầy đủ intkiểu dữ liệu để thực hiện số học không dấu :

Trong Java SE 8 trở lên, bạn có thể sử dụng kiểu dữ liệu int để biểu diễn một số nguyên 32 bit không dấu, có giá trị tối thiểu là 0 và giá trị tối đa là 2 ^ 32-1. Sử dụng lớp Integer để sử dụng kiểu dữ liệu int làm số nguyên không dấu. Các phương thức tĩnh như compareUnsigned, divideUnsignedv.v. đã được thêm vào lớp Integer để hỗ trợ các phép toán số học cho các số nguyên không dấu.

Lưu ý rằng intcác biến vẫn được ký khi khai báo nhưng số học không dấu hiện có thể thực hiện được bằng cách sử dụng các phương thức đó trong Integerlớp.


11
Công bằng mà nói, đối với nhiều dự án, các yêu cầu kỹ thuật không nghiêm ngặt và bạn thực sự có thể đủ khả năng để "lãng phí" bộ nhớ như thế.
Simeon Visser

6
Tôi biết, tôi cũng hiểu mục đích ban đầu của Java. Nhưng ví dụ: Điện thoại thông minh không sử dụng thêm bộ nhớ. Và họ thường sử dụng Java, theo như tôi biết. Nhưng tốt, tôi không muốn bắt đầu một cuộc chiến giữa các lập trình viên Java và những người khác.
Tomáš Zato - Phục hồi Monica

122
Đối với tôi đây không chỉ là một trường hợp lãng phí tiền bạc. Khi bạn làm việc ở cấp độ bit, không dấu đơn giản là dễ dàng hơn để làm việc với
Cruncher

24
Kể từ Java 8, điều này không còn đúng nữa . Trong Java SE 8 trở lên, bạn có thể sử dụng intkiểu dữ liệu để biểu thị một số nguyên 32 bit không dấu, có giá trị tối thiểu 0và giá trị tối đa là 2^32-1. - xem docs.oracle.com/javase/tutorial/java/nutsandbolts/ tôndocs.oracle.com/javase/8/docs/api/java/lang/Integer.html
8bitjunkie

2
@ 7 Đặc biệt: Tôi đã cập nhật câu trả lời để bao gồm thông tin đó. Điều đó đang được nói, không thể khai báo các số nguyên không dấu hoặc loại trừ các giá trị âm, chỉ có thể sử dụng intnhư thể nó không được ký bằng cách sử dụng các phương thức khác nhau.
Simeon Visser

70

1
@stas Tôi đang gặp khó khăn trong việc hiểu việc sử dụng và vấn đề lớn là có khả năng sử dụng các kiểu dữ liệu không dấu. Từ các nguồn trực tuyến khác nhau mà tôi đọc được, có vẻ như nó xoay quanh việc chỉ mở rộng giá trị tối đa và ẩn ý của tự nhiên đảm bảo rằng đó là một số dương. Là sự hiểu biết của tôi chính xác, hoặc có những lý do chính khác? Ngoài ra, bây giờ Integerlớp trong Java 8 cho phép một người sử dụng int unsign, là sự khác biệt giữa chỉ trong không gian và tốc độ (vì trong C / C ++ chúng còn nguyên thủy, trong khi ở Java, nó là một trình bao bọc toàn bộ đối tượng)
Abdul

2
@Abdul - khi bạn làm việc ở cấp độ bit (thường là do bạn đang can thiệp vào phần cứng), bạn cần các giá trị để hành xử theo một cách nhất định. tức là - cuộn lại sau 11111111 đến 00000000, v.v ... Sử dụng chữ ký thay cho dấu không ký có thể phá vỡ các tính toán CRC, v.v. Nó không phải là một nút chặn hiển thị, chỉ lãng phí thời gian.
Lorne K

3
@Lorne K: trong Java, hãy intcuộn lại, ngay cả khi chúng được ký. Đó là C / C ++ khi cuộn không dấu, nhưng đã ký khiến cho hành vi không xác định được hành vi không xác định được. Nếu trên đường lăn lộn trên đường cao tốc là mối quan tâm duy nhất của bạn, bạn không cần phải ký tên. Tôi đoán, đó là lý do tại sao các thói quen CRC, v.v. hoạt động trong Java mà không cần nỗ lực thêm. Và đó là lý do tại sao API mới chỉ thêm phân tích cú pháp, định dạng, so sánh, phân chia và phần còn lại. Tất cả các hoạt động khác, cụ thể là tất cả các thao tác bit, nhưng cũng có phép cộng, phép trừ, phép nhân, v.v ... dù sao cũng đang làm đúng.
Holger

4
@Ciprian Tomoiaga: để thêm bằng cuộn qua, các mẫu bit của đầu vào và kết quả không phụ thuộc vào việc bạn diễn giải nó là số đã ký hay số chưa ký. Nếu bạn có kiên nhẫn, bạn có thể thử nó với tất cả 2⁶⁵ tổ hợp Kết hợp
Holger

3
@Holger cảm ơn đã giải thích! Thật vậy, hóa ra đó là lý do tại sao chúng ta thực sự sử dụng phần bù 2. Tôi đã thử nó với một số kết hợp 2 ^ 8 ^^
Ciprian Tomoiagă

66

Việc một giá trị trong một int được ký hay không dấu phụ thuộc vào cách các bit được diễn giải - Java diễn giải các bit như một giá trị đã ký (nó không có các nguyên hàm không dấu).

Nếu bạn có một int mà bạn muốn hiểu là một giá trị không dấu (ví dụ: bạn đọc một int từ DataInputStream mà bạn biết có chứa một giá trị không dấu) thì bạn có thể thực hiện thủ thuật sau.

int fourBytesIJustRead = someObject.getInt();
long unsignedValue = fourBytesIJustRead & 0xffffffffl;

Lưu ý, điều quan trọng là chữ hex là một chữ dài, không phải là chữ int - do đó 'l' ở cuối.


3
Đối với tôi đây là câu trả lời tốt nhất ... Dữ liệu của tôi đến từ UID thẻ NFC, có thể có 4 hoặc 8 byte ... Trong trường hợp 4 byte tôi cần chuyển nó thành một số nguyên không dấu và tôi không thể sử dụng ByteBuffer.getLong vì đó không phải là dữ liệu 64 bit. Cảm ơn.
Lâu đài

Tại sao nó cần phải dài. Bạn không thể làm 0xFFFFFFvà giữ int?
Displee

19

Chúng tôi cần số unsigned để mô hình MySQL của unsigned TINYINT, SMALLINT, INT, BIGINTtrong jOOQ , đó là lý do chúng tôi đã tạo jOOU , một lời đề nghị thư viện Minimalistic wrapper loại cho số unsigned integer trong Java. Thí dụ:

import static org.joou.Unsigned.*;

// and then...
UByte    b = ubyte(1);
UShort   s = ushort(1);
UInteger i = uint(1);
ULong    l = ulong(1);

Tất cả các loại này mở rộng java.lang.Numbervà có thể được chuyển đổi thành các loại nguyên thủy bậc cao hơn và BigInteger. Hi vọng điêu nay co ich.

(Tuyên bố miễn trừ trách nhiệm: Tôi làm việc cho công ty đằng sau các thư viện này)


Điều này nghe có vẻ rất thuận tiện! Cảm ơn đã đề cập. :)
Lucas Sousa

7

Đối với các số chưa được ký, bạn có thể sử dụng các lớp này từ thư viện Guava :

Họ hỗ trợ các hoạt động khác nhau:

  • thêm
  • dấu trừ
  • lần
  • mod
  • chia

Thứ dường như còn thiếu tại thời điểm này là các toán tử dịch chuyển byte. Nếu bạn cần những thứ đó, bạn có thể sử dụng BigInteger từ Java.



2

Có lẽ đây là những gì bạn có ý nghĩa?

long getUnsigned(int signed) {
    return signed >= 0 ? signed : 2 * (long) Integer.MAX_VALUE + 2 + signed;
}
  • getUnsigned(0) → 0
  • getUnsigned(1) → 1
  • getUnsigned(Integer.MAX_VALUE) → 2147483647
  • getUnsigned(Integer.MIN_VALUE) → 2147483648
  • getUnsigned(Integer.MIN_VALUE + 1) → 2147483649

Bạn đang hy sinh một phần trăm của một giây thời gian thực hiện cho việc lười gõ với các toán tử tạm thời thay vì các câu lệnh if. Không tốt. (đùa)
ytpillai

5
Bạn có thực sự nghĩ rằng, 2 * (long) Integer.MAX_VALUE + 2dễ hiểu hơn 0x1_0000_0000L? Về vấn đề đó, tại sao không đơn giản return signed & 0xFFFF_FFFFL;?
Holger

2

Dường như bạn có thể xử lý vấn đề ký bằng cách thực hiện "logic VÀ" trên các giá trị trước khi bạn sử dụng chúng:

Ví dụ (Giá trị byte[] header[0]0x86):

System.out.println("Integer "+(int)header[0]+" = "+((int)header[0]&0xff));

Kết quả:

Integer -122 = 134

2

Có câu trả lời tốt ở đây, nhưng tôi không thấy bất kỳ cuộc biểu tình nào về hoạt động bitwise. Giống như Visser (câu trả lời hiện được chấp nhận) cho biết, Java ký các số nguyên theo mặc định (Java 8 có các số nguyên không dấu, nhưng tôi chưa bao giờ sử dụng chúng). Nếu không có thêm rắc rối, hãy làm điều đó ...

Ví dụ RFC 868

Điều gì xảy ra nếu bạn cần viết một số nguyên không dấu vào IO? Ví dụ thực tế là khi bạn muốn xuất thời gian theo RFC 868 . Điều này đòi hỏi một số nguyên 32 bit, endian lớn, không dấu, mã hóa số giây kể từ 12:00 AM ngày 1 tháng 1 năm 1900. Bạn sẽ mã hóa số này như thế nào?

Tạo số nguyên 32 bit không dấu của riêng bạn như thế này:

Khai báo một mảng 4 byte (32 bit)

Byte my32BitUnsignedInteger[] = new Byte[4] // represents the time (s)

Điều này khởi tạo mảng, xem Các mảng byte có được khởi tạo về 0 trong Java không? . Bây giờ bạn phải điền từng byte trong mảng với thông tin theo thứ tự lớn (hoặc endian nhỏ nếu bạn muốn phá hủy sự tàn phá). Giả sử bạn có thời gian chứa thời gian dài (số nguyên dài dài 64 bit trong Java) được gọi secondsSince1900(Chỉ sử dụng giá trị 32 bit đầu tiên và bạn đã xử lý thực tế là Ngày tham chiếu 12:00 AM ngày 1 tháng 1 năm 1970), sau đó bạn có thể sử dụng logic AND để trích xuất các bit từ nó và chuyển các bit đó thành các vị trí (chữ số) sẽ không bị bỏ qua khi được đặt vào Byte và theo thứ tự lớn.

my32BitUnsignedInteger[0] = (byte) ((secondsSince1900 & 0x00000000FF000000L) >> 24); // first byte of array contains highest significant bits, then shift these extracted FF bits to first two positions in preparation for coersion to Byte (which only adopts the first 8 bits)
my32BitUnsignedInteger[1] = (byte) ((secondsSince1900 & 0x0000000000FF0000L) >> 16);
my32BitUnsignedInteger[2] = (byte) ((secondsSince1900 & 0x000000000000FF00L) >> 8);
my32BitUnsignedInteger[3] = (byte) ((secondsSince1900 & 0x00000000000000FFL); // no shift needed

my32BitUnsignedIntegerBây giờ của chúng tôi tương đương với một số nguyên lớn 32 bit không dấu, tuân thủ tiêu chuẩn RCF 868. Có, kiểu dữ liệu dài đã được ký, nhưng chúng tôi đã bỏ qua thực tế đó, vì chúng tôi giả sử rằng giâySince1900 chỉ sử dụng 32 bit thấp hơn). Do kết hợp độ dài thành một byte, tất cả các bit cao hơn 2 ^ 7 (hai chữ số đầu tiên trong hex) sẽ bị bỏ qua.

Nguồn tham khảo: Lập trình mạng Java, Phiên bản thứ 4.


1

Chỉ cần tạo đoạn mã này, sẽ chuyển đổi "this.altura" từ số âm sang số dương. Hy vọng điều này sẽ giúp ai đó cần giúp đỡ

       if(this.altura < 0){    

                        String aux = Integer.toString(this.altura);
                        char aux2[] = aux.toCharArray();
                        aux = "";
                        for(int con = 1; con < aux2.length; con++){
                            aux += aux2[con];
                        }
                        this.altura = Integer.parseInt(aux);
                        System.out.println("New Value: " + this.altura);
                    }

-19

Bạn có thể sử dụng hàm Math.abs (số). Nó trả về một số dương.


12
nitpick: không nếu bạn vượt quaMIN_VALUE
Dennis Meng

2
@ kyo722 Tôi không thể tưởng tượng rằng điều này sẽ trả về một giá trị dương trong phạm vi nguyên thủy không dấu.
Florian R. Klein

1
nitpick # 2: not if you pass in0
genisage 22/1/2015
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.