Có thể phân biệt giữa 0 và -0 không?


94

Tôi biết rằng các giá trị số nguyên 0-0về cơ bản là giống nhau. Nhưng, tôi đang tự hỏi liệu có thể phân biệt giữa chúng không.

Ví dụ, làm cách nào để biết liệu một biến đã được chỉ định -0hay chưa?

bool IsNegative(int num)
{
    // How ?
}

int num = -0;
int additinon = 5;

num += (IsNegative(num)) ? -addition : addition;

Giá trị được -0lưu trong bộ nhớ có chính xác như cách 0không?


9
Đối với số nguyên, không có sự khác biệt.
Maroun

14
Điều này phụ thuộc vào việc triển khai, nhưng đối với các triển khai intđược biểu diễn trong phần bổ sung của 2 (cho đến nay là thường gặp nhất) 0-0có các biểu diễn bitwise giống hệt nhau.
Mankarse

11
Trên máy bổ sung của 2 không có sự khác biệt ở cấp độ bit.
Marco A.

17
@VirtualSnake: "Trong hệ nhị phân" nghĩa là gì? Có, trên thực tế, mã hóa nhị phân mà có một sự phân biệt giữa -0 và 0. Ký tên và tầm quan trọng, ví dụ.
Benjamin Lindley,

8
@VirtualSnake Đúng vậy, chúng ta đang nói đến int. Xem phần mã hóa bổ sung của Ones .
CiaPan

Câu trả lời:


112

Nó phụ thuộc vào máy bạn đang nhắm mục tiêu.

Trên một máy sử dụng biểu diễn phần bù của 2 cho số nguyên, không có sự khác biệt ở cấp độ bit giữa 0-0(chúng có cùng cách biểu diễn)

Nếu máy của bạn sử dụng phần bổ sung của một người , bạn chắc chắn có thể

0000 0000   -> signed01111 1111   -> signed   0

Rõ ràng là chúng ta đang nói về việc sử dụng hỗ trợ gốc , các bộ xử lý dòng x86 có hỗ trợ riêng cho biểu diễn bổ sung của hai số có dấu. Sử dụng các biểu diễn khác chắc chắn là có thể nhưng có lẽ sẽ kém hiệu quả hơn và cần nhiều hướng dẫn hơn.

(Như JerryCoffin cũng đã lưu ý: ngay cả khi phần bổ sung của một người được coi là chủ yếu vì các lý do lịch sử, thì các biểu diễn cường độ có chữ ký vẫn khá phổ biến và có một đại diện riêng biệt cho số 0 âm và dương)


6
@TobiMcNamobi: Không đủ khả năng để lo lắng về điều đó. Tôi sẽ rất ngạc nhiên nếu ai đó đã từng bận tâm đến việc chuyển một trình biên dịch C ++ để tạo ra đầu ra cho một chiếc máy như vậy.
Benjamin Lindley,

1
Tôi đồng ý với Benjamin, trong lịch sử đã từng có máy móc sử dụng nó, nhưng ngày nay tôi không tình cờ biết máy sản xuất sử dụng nó. Tuy nhiên, luôn luôn tốt nếu biết và ghi nhớ.
Marco A.

4
@TobiMcNamobi của một người bổ sung vẫn được sử dụng trong Unisys 2200 hệ thống stackoverflow.com/a/12277974/995714 stackoverflow.com/q/6971886/995714
phuclv

2
Tôi chưa bao giờ xem xét các yêu cầu bổ sung của một người - liệu tiêu chuẩn có thực sự đảm bảo điều đó 0-0có gì khác không? Thành thật mà nói, tôi đã mong đợi nó hoạt động giống như cho phép hai biểu diễn bit có cùng giá trị và chương trình của bạn có thể sử dụng bất kỳ giá trị nào mà nó cảm thấy thích.

8
@Hurkly: không, ngay cả khi tồn tại biểu diễn số 0 âm, tiêu chuẩn không đảm bảo rằng việc gán hoặc khởi tạo bằng biểu thức -0, nghĩa là kết quả của việc áp dụng toán -tử một ngôi cho hằng số nguyên 0, là biểu diễn số 0 âm. Bất kể biểu diễn là gì, tiêu chuẩn không bao giờ nói 0-0là các giá trị khác nhau về mặt toán học, chỉ là có thể có một mẫu bit âm-0. Nếu có, nó vẫn đại diện cho cùng một giá trị số, 0.
Steve Jessop

14

Đối với một int(trong biểu diễn "phần bù của 2" gần như phổ biến) các biểu diễn của 0-0giống nhau. (Chúng có thể khác nhau đối với các biểu diễn số khác, ví dụ. Dấu chấm động IEEE 754.)


9
>> Giả sử đại diện bổ sung của 2
Marco A.

12

Hãy bắt đầu với việc biểu diễn số 0 trong phần bù của 2 (tất nhiên tồn tại nhiều hệ thống và cách biểu diễn khác, ở đây tôi đang đề cập đến hệ thống cụ thể này), giả sử 8-bit, số 0 là:

0000 0000

Bây giờ, hãy lật tất cả các bit và thêm 1 để có phần bổ sung của 2:

1111 1111 (flip)
0000 0001 (add one)
---------
0000 0000

chúng tôi đã nhận 0000 0000, và đó cũng là đại diện của -0.

Nhưng lưu ý rằng trong phần bù của 1, dấu 0 là 0000 0000, nhưng -0 là 1111 1111.


1
Tôi có thể biết lý do tại sao các phiếu phản đối để cải thiện câu trả lời của tôi không?
Maroun

1
Mặc dù hầu hết các câu trả lời khác đều đúng về mặt kỹ thuật, nhưng câu trả lời của bạn là thực tế và cung cấp một cách triển khai. Tốt.
umlcat

9

Tôi đã quyết định để lại câu trả lời này vì việc triển khai C và C ++ thường có liên quan chặt chẽ với nhau, nhưng trên thực tế, nó không tuân theo tiêu chuẩn C như tôi nghĩ. Điểm vẫn còn là tiêu chuẩn C ++ không chỉ định điều gì xảy ra cho những trường hợp như thế này. Nó cũng có liên quan rằng các biểu diễn không bổ sung hai phần cực kỳ hiếm trong thế giới thực và ngay cả khi chúng tồn tại, chúng thường che giấu sự khác biệt trong nhiều trường hợp hơn là để lộ nó như một thứ mà ai đó có thể dễ dàng phát hiện ra.


Hành vi của các số không âm trong các biểu diễn số nguyên mà chúng tồn tại không được xác định chặt chẽ trong tiêu chuẩn C ++ như trong tiêu chuẩn C. Tuy nhiên, nó viện dẫn tiêu chuẩn C (ISO / IEC 9899: 1999) như một tài liệu tham khảo quy chuẩn ở cấp cao nhất [1.2].

Trong tiêu chuẩn C [6.2.6.2], số 0 âm chỉ có thể là kết quả của các phép toán bit hoặc các phép toán đã có số 0 âm (ví dụ: nhân hoặc chia số 0 âm cho một giá trị hoặc thêm số 0 âm vào zero) - áp dụng toán tử trừ một bậc cho giá trị của một số 0 bình thường, như trong ví dụ của bạn, do đó, được đảm bảo sẽ dẫn đến một số 0 bình thường.

Ngay cả trong những trường hợp có thể tạo ra số 0 âm, không có gì đảm bảo rằng chúng sẽ làm được, ngay cả trên một hệ thống hỗ trợ số 0 âm:

Không xác định được liệu những trường hợp này thực sự tạo ra số 0 âm hay số 0 bình thường và liệu số 0 âm có trở thành số 0 bình thường khi được lưu trữ trong một đối tượng hay không.

Do đó, chúng ta có thể kết luận: không, không có cách nào đáng tin cậy để phát hiện trường hợp này. Ngay cả khi không có thực tế là các biểu diễn không bổ sung hai phần là rất phổ biến trong các hệ thống máy tính hiện đại.

Về phần mình, tiêu chuẩn C ++ không đề cập đến thuật ngữ "số không âm" và có rất ít thảo luận về các chi tiết của cường độ có dấu và các biểu diễn bổ sung của một người, ngoại trừ lưu ý [3.9.1 đoạn 7] rằng chúng được phép.


Nói chung là không, thực tế là một cái gì đó đúng / bắt buộc trong C không nhất thiết có nghĩa là nó đúng / bắt buộc trong C ++. Thực tế là C là một tham chiếu chuẩn có nghĩa là C ++ đề cập đến chuẩn C cho nhiều thứ khác nhau (chủ yếu là nội dung của tiêu đề chuẩn), nhưng định nghĩa về kiểu số nguyên không phải là một trong những thứ đó. Tuy nhiên, việc không có cách đảm bảo để tạo ra số 0 âm có nghĩa là những gì bạn kết luận vẫn đúng, không có cách nào chắc chắn để tạo ra một số học ngay cả khi biểu diễn tồn tại.
Steve Jessop,

Vậy tại sao tiêu chuẩn C ++ lại ít chi tiết hơn về những thứ như thế này?
Ngẫu nhiên832

1
Sở thích cá nhân, tôi nghĩ, nếu số lượng người bỏ phiếu cho tiêu chuẩn C ++ có thể được coi là "cá nhân" :-) Tuy nhiên, nếu nó sẽ trì hoãn tiêu chuẩn C cho các định nghĩa, thì nó có thể làm tốt công việc của nó và không chứa chi tiết, như trong một số trường hợp khác.
Steve Jessop,

Có phải "C ++ là ngôn ngữ lập trình mục đích chung dựa trên ngôn ngữ lập trình C như được mô tả trong ISO / IEC 9899: 1999 Ngôn ngữ lập trình - C (sau đây được gọi là tiêu chuẩn C)." [1.1 đoạn 2] có bất kỳ ý nghĩa quy phạm nào không? Tôi nghĩ rằng điều đó nhằm mục đích kết hợp chung tiêu chuẩn C cho bất kỳ thứ gì không bị tiêu chuẩn C ++ ghi đè cụ thể.
Ngẫu nhiên832

@ Random832 Không. Nó chỉ là một ghi chú lịch sử (chẳng hạn như không có _Boolhoặc _Complexhoặc các bộ khởi tạo được chỉ định hoặc các ký tự ghép trong C ++). Tiêu chuẩn C ++ biết cách kết hợp tiêu chuẩn C khi nó muốn - ví dụ: [basic.fundaries] / p3: "Các kiểu số nguyên có dấu và không dấu phải thỏa mãn các ràng buộc được đưa ra trong tiêu chuẩn C, mục 5.2.4.2.1."
TC

8

Nếu máy của bạn có các đại diện riêng biệt cho -0+0thì memcmpsẽ có thể phân biệt chúng.

Nếu có các bit đệm, trên thực tế cũng có thể có nhiều biểu diễn cho các giá trị khác 0.


5

Trong đặc tả ngôn ngữ C ++, không có số nguyên nào như số không âm .

Ý nghĩa duy nhất của hai từ đó là toán tử một ngôi - được áp dụng cho 0, cũng như ba cộng năm chỉ là toán tử nhị phân +được áp dụng cho 35.

Nếu có một số 0 âm riêng biệt , phần bù của hai (cách biểu diễn phổ biến nhất của các kiểu số nguyên) sẽ là một biểu diễn không đủ cho các triển khai C ++, vì không có cách nào để biểu diễn hai dạng số 0.


Ngược lại, dấu chấm động (theo IEEE) có số 0 âm và dương riêng biệt. Ví dụ, chúng có thể được phân biệt khi chia 1 cho chúng. Số 0 dương tạo ra dương vô cùng; số không âm tạo ra âm vô cùng.


Tuy nhiên, nếu xảy ra các biểu diễn bộ nhớ khác nhau của int 0 (hoặc bất kỳ int nào hoặc bất kỳ giá trị nào khác thuộc bất kỳ kiểu nào khác), bạn có thể sử dụng memcmpđể khám phá rằng:

#include <string>

int main() {
    int a = ...
    int b = ...
    if (memcmp(&a, &b, sizeof(int))) {
        // a and b have different representations in memory
    }
}

Tất nhiên, nếu điều này xảy ra, ngoài các hoạt động bộ nhớ trực tiếp, hai giá trị sẽ vẫn hoạt động theo cùng một cách.


3
Thực ra, ngôn ngữ không bắt buộc sự tồn tại của nó không có nghĩa là nó bắt buộc sự vắng mặt của nó. Gợi ý: Nó không bắt buộc.
Deduplicator

2
@Deduplicator, đại loại là. Ý tôi là "trong ngôn ngữ C ++", "trong đặc tả ngôn ngữ C ++ ". Vì không có đề cập đến froobinators trong thông số kỹ thuật, tôi có thể nói "C ++ không có froobinators" mà không có quá nhiều mơ hồ. Tôi nghĩ điều đó đã rõ ràng, nhưng tôi sẽ cải thiện nó.
Paul Draper,

1
Thông số ngôn ngữ cũng không đề cập đến kỳ lân.
ypercubeᵀᴹ

2

Để đơn giản hóa, tôi thấy nó dễ hình dung hơn.

Kiểu int (_32) được lưu trữ với 32 bit . 32 bit có nghĩa là 2 ^ 32 = 4294967296 giá trị duy nhất . Như vậy:

dải dữ liệu unsigned int0 đến 4,294,967,295

Trong trường hợp giá trị âm, nó phụ thuộc vào cách chúng được lưu trữ. Trong trường hợp

Trong trường hợp bổ sung của Một giá trị tồn tại -0.


2
Tôi đã không phản đối nhưng các nền tảng intkhông được lưu trữ trong 32 bit ngày nay phổ biến hơn các nền tảng có bổ sung.
Maciej Piechotka
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.