2015-09-09 18 views
23

Kết quả sau khiến tôi thực sự bối rối:Trừ giữa signed và unsigned tiếp theo bộ phận

int i1 = 20-80u; // -60 
int i2 = 20-80;  // -60 
int i3 =(20-80u)/2; // 2147483618 
int i4 =(20-80)/2; // -30 
int i5 =i1/2;  // -30 
  1. i3 dường như được tính như (20u-80u)/2, thay vì (20-80u)/2
  2. cho là i3 cũng giống như i5.

Trả lời

12

IIRC, thao tác số học giữa int đã ký và chưa ký sẽ tạo ra kết quả chưa được ký.

Như vậy, 20 - 80u tạo ra kết quả tương đương với unsigned -60: nếu unsigned int là một loại 32-bit, kết quả đó là 4294967236.

Ngẫu nhiên, gán đó để tạo ra một i1thực hiện xác định kết quả vì số quá lớn để vừa vặn. Nhận -60 là điển hình, nhưng không được bảo đảm.

+4

_Tuy nhiên, gán giá trị đó cho i1 là hành vi không xác định_ Bạn có chắc chắn về điều đó không? Tôi dạy rằng chuyển đổi từ int unsigned để ký int cũng được xác định cho tất cả các giá trị của unsigned int. – rozina

+3

Không có tràn số nguyên đã ký ở đây. Có chuyển đổi. Xem [conv.integral] (http://eel.is/c++draft/conv.integral). – Sebivor

+0

@rozina: Huh, tôi chưa từng thấy chuyển đổi đó hoạt động khác theo khía cạnh này. Cố định – Hurkyl

10
int i1 = 20-80u; // -60 

Các toán hạng là khác nhau, do đó, một chuyển đổi là cần thiết. Cả hai toán hạng được chuyển đổi thành một kiểu chung (một số unsigned int, trong trường hợp này). Kết quả sẽ là giá trị unsigned int lớn (60 nhỏ hơn UINT_MAX + 1 nếu tính toán của tôi là chính xác) sẽ được chuyển thành int trước khi được lưu trữ trong i1. Vì giá trị đó nằm ngoài phạm vi của int, kết quả sẽ được thực hiện được xác định, có thể là một đại diện bẫy và do đó có thể gây ra hành vi không xác định khi bạn cố gắng sử dụng nó. Tuy nhiên, trong trường hợp của bạn, tình cờ nó chuyển đổi thành -60.


int i3 =(20-80u)/2; // 2147483618 

Tiếp tục trên từ ví dụ đầu tiên, tôi đoán là kết quả của 20-80u sẽ là 60 ít hơn UINT_MAX + 1. Nếu UINT_MAX là 4294967295 (một giá trị chung cho UINT_MAX), điều đó có nghĩa là 20-80u4294967236 ... và 4294967236/2 là 2147483618.


Đối với i2 và những người khác, không nên có bất ngờ. Họ làm theo các phép tính toán học thông thường mà không có chuyển đổi, cắt bớt hoặc tràn.

+0

Vì vậy, nếu tôi hiểu điều này một cách chính xác, chuyển đổi -1 thành unsigned cũng được xác định và nó là UINT_MAX.Nhưng nếu bạn sau đó chuyển đổi UINT_MAX trở lại int nó đột nhiên thực hiện được xác định? Và không thể là -1? – rozina

+0

@rozina Điều đó đúng. – Sebivor

+0

Ngày trả lời tốt đẹp :) – LPs

3

Toán tử số học nhị phân sẽ thực hiện usual arithmetic conversions trên toán hạng của chúng để mang chúng đến một kiểu chung.

Trong trường hợp của i1, i3i5 loại phổ biến sẽ unsigned int và do đó kết quả sẽ còn được unsigned int. Số chưa ký sẽ được bọc qua số học modulo và do đó trừ đi một giá trị chưa ký lớn hơn một chút sẽ dẫn đến một số gần với int tối đa không dấu mà không thể được đại diện bởi một int.

Vì vậy, trong trường hợp i1, chúng tôi kết thúc bằng chuyển đổi được triển khai do giá trị không thể được biểu diễn. Trong trường hợp của i3 chia cho 2 đưa giá trị chưa ký trở lại vào phạm vi của int và vì vậy chúng tôi kết thúc với giá trị int đã ký lớn sau khi chuyển đổi.

Các phần có liên quan tạo thành tiêu chuẩn dự thảo C++ như sau. Mục 5.7[expr.add]:

Các nhà khai thác phụ gia + và - nhóm từ trái sang phải. Chuyển đổi số học thông thường được thực hiện cho toán hạng của số học hoặc kiểu liệt kê.

Quá trình chuyển đổi số học thông thường được bảo hiểm trong phần 5 và nó nói:

Nhiều nhà khai thác nhị phân mà mong đợi toán hạng số học hoặc liệt kê loại nguyên nhân chuyển đổi và mang lại loại kết quả theo một cách tương tự. Mục đích là để mang lại một loại phổ biến, đó cũng là loại kết quả. mô hình này được gọi là chuyển đổi số học thông thường, được định nghĩa như sau:

[...]

  • Ngược lại, nếu các toán hạng có kiểu dữ liệu integer unsigned có xếp hạng lớn hơn hoặc bằng với xếp hạng của loại toán hạng khác, toán hạng có loại số nguyên đã ký sẽ được chuyển thành loại toán hạng với loại số nguyên không dấu.

và cho việc chuyển đổi từ một giá trị mà không thể được đại diện cho một loại ký, phần 4.7[conv.integral]:

Nếu loại đích được ký kết, giá trị không thay đổi nếu nó có thể được thể hiện trong loại đích (và chiều rộng trường bit); nếu không, giá trị được xác định thực hiện.

và cho số nguyên unsigned tuân theo modulo số học phần 3.9.1[basic.fundamental]:

số nguyên Unsigned phải tuân theo pháp luật của số học modulo 2n trong đó n là số bit trong giá trị đại diện cho kích thước cụ thể đó của số nguyên.48

+0

@Hurkyl: Chết tiệt, tôi đang ngủ đứng ngày hôm nay, tôi đã xáo trộn tràn không được ký và chuyển đổi từ unsigned để ký (sau này được thực hiện xác định). Tôi sẽ tự hủy bỏ bình luận của mình ... –

Các vấn đề liên quan