Quy tắc chuyển đổi kiểu ngầm định trong toán tử C ++


167

Tôi muốn được tốt hơn về việc biết khi nào tôi nên chọn. Các quy tắc chuyển đổi kiểu ngầm định trong C ++ là gì khi thêm, nhân, v.v.

int + float = ?
int * float = ?
float * int = ?
int / float = ?
float / int = ?
int / int = ?
int ^ float = ?

vân vân...

Biểu thức sẽ luôn được đánh giá là loại chính xác hơn? Các quy tắc khác nhau cho Java? Xin vui lòng sửa cho tôi nếu tôi đã đặt câu hỏi này không chính xác.


15
Hãy ghi nhớ ^là XOR.
GManNickG

15
@int ^ float = lỗi biên dịch :)
Serge Dundich

Câu trả lời:


222

Trong các toán tử C ++ (đối với các loại POD) luôn hành động trên các đối tượng cùng loại.
Do đó, nếu chúng không giống nhau, một cái sẽ được thăng cấp để phù hợp với cái khác.
Loại kết quả của hoạt động giống như toán hạng (sau khi chuyển đổi).

If either is      long          double the other is promoted to      long          double
If either is                    double the other is promoted to                    double
If either is                    float  the other is promoted to                    float
If either is long long unsigned int    the other is promoted to long long unsigned int
If either is long long          int    the other is promoted to long long          int
If either is long      unsigned int    the other is promoted to long      unsigned int
If either is long               int    the other is promoted to long               int
If either is           unsigned int    the other is promoted to           unsigned int
If either is                    int    the other is promoted to                    int
Both operands are promoted to int

Ghi chú. Kích thước tối thiểu của hoạt động là int. Vì vậy, short/ charđược thăng cấp inttrước khi hoạt động được thực hiện.

Trong tất cả các biểu thức của bạn, intnó được thăng cấp lên floattrước khi thao tác được thực hiện. Kết quả của hoạt động là a float.

int + float =>  float + float = float
int * float =>  float * float = float
float * int =>  float * float = float
int / float =>  float / float = float
float / int =>  float / float = float
int / int                     = int
int ^ float =>  <compiler error>

1
"Kích thước tối thiểu của hoạt động là int." - Điều này sẽ rất lạ (những gì về kiến ​​trúc hỗ trợ hiệu quả các hoạt động char / short?) Đây có thực sự là trong thông số C ++ không?
Rafał Dowgird

3
@Rafal: Vâng. int được coi là loại số nguyên hiệu quả nhất cho hoạt động trên một nền tảng cụ thể. char phải luôn là 1 nhưng ngắn có thể có cùng kích thước với int.
Martin York

1
@ Rafał: vâng, nó rất lạ và nó nằm trong tiêu chuẩn. Trong rất nhiều trường hợp, kiến ​​trúc bạn mô tả có thể sử dụng loại siêu hiệu quả của nó char. Nếu giá trị của char + charđược gán cho a char, thì nó chỉ có thể thực hiện số học trong charvà ví dụ bao quanh. Nhưng nếu kết quả được gán cho intthì nó phải thực hiện số học theo kiểu đủ lớn để có kết quả chính xác khi nó nhiều hơn CHAR_MAX.
Steve Jessop

2
Tôi chỉ muốn nhấn mạnh một thực tế là int được thăng cấp thành int unsign !!! Tôi đã vật lộn với các lỗi trong nhiều ngày bởi vì tôi có ấn tượng rằng cả hai sẽ được thăng cấp thành int hoặc lâu để kết quả tiêu cực có thể sẽ không gây ra tràn / bao bọc.
nitsas

10
Ví dụ về vấn đề " int được thăng cấp thành int unsign ": ((int) 4) - ((unsigned int) 5)sẽ dẫn đến int int4294967295 32 bit và ints không dấu 32 bit.
nitsas

33

Các phép toán số học liên quan đến floatkết quả trong float.

int + float = float
int * float = float
float * int = float
int / float = float
float / int = float
int / int = int

Để biết thêm chi tiết câu trả lời. Hãy xem phần §5 / 9 từ Tiêu chuẩn C ++ nói gì

Nhiều toán tử nhị phân mong đợi toán hạng của loại số học hoặc liệt kê gây ra chuyển đổi và mang lại các loại kết quả theo 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ẫu này được gọi là chuyển đổi số học thông thường, được định nghĩa như sau:

- Nếu một trong hai toán hạng có kiểu dài gấp đôi, thì toán hạng kia sẽ được chuyển thành dài gấp đôi.

- Mặt khác, nếu một trong hai toán hạng là gấp đôi, thì toán tử kia sẽ được chuyển đổi thành gấp đôi.

- Mặt khác, nếu một trong hai toán hạng là float, thì cái kia sẽ được chuyển thành float.

- Mặt khác, các chương trình khuyến mãi tích hợp (4.5) sẽ được thực hiện trên cả hai toán hạng.54)

- Sau đó, nếu một trong hai toán hạng không được ký dài thì cái kia sẽ được chuyển thành dài không dấu.

- Mặt khác, nếu một toán hạng là một int dài và int không dấu khác, thì nếu một int dài có thể biểu thị tất cả các giá trị của một int unsign, int unsign sẽ được chuyển đổi thành int dài; mặt khác, cả hai toán hạng sẽ được chuyển đổi thành int dài không dấu.

- Mặt khác, nếu một toán hạng dài, thì toán hạng kia sẽ được chuyển thành dài.

- Mặt khác, nếu một trong hai toán hạng không được ký thì cái kia sẽ được chuyển thành không dấu.

[Lưu ý: mặt khác, trường hợp duy nhất còn lại là cả hai toán hạng là int]


3
... miễn là loại kia doublecũng không phải long double.
CB Bailey

1
@Charles: Đúng. Tôi đã trích dẫn phần có liên quan từ Tiêu chuẩn để làm rõ hơn.
Nawaz

Vì vậy, một số nguyên luôn luôn có thể được chuyển đổi để nổi mà không mất dữ liệu? (ví dụ bằng cách không nhập số mũ và sử dụng mọi thứ cho lớp phủ)?
Marco A.

1
Câu trả lời này đã lỗi thời. Đề nghị cập nhật. Đặc biệt, long longunsigned longkhông được giải quyết ngay tại đây.
chux - Phục hồi Monica

@MarcoA. 32 bit floatkhông có đủ bit trong lớp phủ (24 bit cho IEEE-754 ) cho 32 bit int, do đó có thể bị mất dữ liệu. Một 64-bit doublenên là tốt.
Đánh dấu tiền chuộc

17

Vì các câu trả lời khác không nói về các quy tắc trong C ++ 11 ở đây. Từ tiêu chuẩn C ++ 11 (dự thảo n3337) §5 / 9 (nhấn mạnh sự khác biệt):

Mẫu này được gọi là chuyển đổi số học thông thường , được định nghĩa như sau:

- Nếu một trong hai toán hạng thuộc loại liệt kê phạm vi, không có chuyển đổi nào được thực hiện; nếu toán hạng khác không có cùng kiểu, biểu thức không được định dạng.

- Nếu một trong hai toán hạng có kiểu dài gấp đôi, thì toán hạng kia sẽ được chuyển thành dài gấp đôi.

- Mặt khác, nếu một trong hai toán hạng là gấp đôi, thì toán tử kia sẽ được chuyển đổi thành gấp đôi.

- Mặt khác, nếu một trong hai toán hạng là float, thì cái kia sẽ được chuyển thành float.

- Mặt khác, các chương trình khuyến mãi tích hợp sẽ được thực hiện trên cả hai toán hạng. Sau đó, các quy tắc sau sẽ được áp dụng cho các toán hạng được thăng cấp:

- Nếu cả hai toán hạng có cùng loại, không cần chuyển đổi thêm.

- Mặt khác, nếu cả hai toán hạng đều có kiểu số nguyên hoặc cả hai đều có kiểu số nguyên không dấu, toán hạng có loại xếp hạng chuyển đổi số nguyên nhỏ hơn sẽ được chuyển đổi thành loại toán hạng có thứ hạng lớn hơn.

- Mặt khác, nếu toán hạng có loại số nguyên không dấu có thứ hạng lớn hơn hoặc bằng thứ 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 đổi thành loại toán hạng có loại số nguyên không dấu.

- Mặt khác, nếu loại toán hạng có loại số nguyên đã ký có thể biểu thị tất cả các giá trị của loại toán hạng có loại số nguyên không dấu, toán hạng có loại số nguyên không dấu sẽ được chuyển đổi thành loại toán hạng có loại số nguyên đã ký.

- Mặt khác, cả hai toán hạng sẽ được chuyển đổi thành kiểu số nguyên không dấu tương ứng với loại toán hạng có kiểu số nguyên đã ký.

Xem ở đây để xem danh sách thường xuyên được cập nhật.


1
Các quy tắc này giống nhau trong tất cả các phiên bản của C ++, ngoại trừ các liệt kê có phạm vi được thêm vào trong C ++ 11 tất nhiên
MM

6

Câu trả lời này được đạo diễn phần lớn tại một bình luận được thực hiện bởi @ RafałDowgird:

"Kích thước tối thiểu của hoạt động là int." - Điều này sẽ rất lạ (những gì về kiến ​​trúc hỗ trợ hiệu quả các hoạt động char / short?) Đây có thực sự là trong thông số C ++ không?

Hãy nhớ rằng tiêu chuẩn C ++ có quy tắc "như thể nếu" rất quan trọng. Xem phần 1.8: Thực thi chương trình:

3) Điều khoản này đôi khi được gọi là quy tắc "như thể nếu", bởi vì việc triển khai được tự do bỏ qua mọi yêu cầu của Tiêu chuẩn miễn là kết quả như thể yêu cầu đã được tuân thủ, theo như có thể xác định được từ quan sát được hành vi của chương trình.

Trình biên dịch không thể đặt intkích thước là 8 bit, ngay cả khi nó là nhanh nhất, vì tiêu chuẩn bắt buộc tối thiểu 16 bit int.

Do đó, trong trường hợp máy tính lý thuyết có hoạt động 8 bit siêu nhanh, việc quảng bá ngầm intcho số học có thể có vấn đề. Tuy nhiên, đối với nhiều hoạt động, bạn không thể biết liệu trình biên dịch có thực sự thực hiện các thao tác với độ chính xác của một intvà sau đó được chuyển đổi thành một charlưu trữ trong biến của bạn hay không, nếu các hoạt động được thực hiện trong char cùng.

Ví dụ, hãy xem xét unsigned char = unsigned char + unsigned char + unsigned char, nơi bổ sung sẽ tràn (giả sử giá trị 200 cho mỗi). Nếu bạn được thăng cấp int, bạn sẽ nhận được 600, sau đó sẽ được chuyển hoàn toàn thành một unsigned char, bao bọc modulo 256, do đó sẽ cho kết quả cuối cùng là 88. Nếu bạn không có chương trình khuyến mãi nào như vậy, bạn sẽ phải kết thúc giữa lần đầu tiên hai bổ sung, sẽ làm giảm vấn đề từ 200 + 200 + 200đến 144 + 200, là 344, giảm xuống còn 88. Nói cách khác, chương trình không biết sự khác biệt, vì vậy trình biên dịch có thể bỏ qua nhiệm vụ thực hiện các hoạt động trung gian trong intnếu toán hạng có một thứ hạng thấp hơn int.

Điều này đúng nói chung về phép cộng, phép trừ và phép nhân. Nói chung, nó không đúng đối với phép chia hoặc mô đun.


4

Nếu bạn loại trừ các loại không dấu, có một hệ thống phân cấp có thứ tự: char đã ký, short, int, dài, dài dài, float, double, long double. Đầu tiên, mọi thứ đến trước int ở trên sẽ được chuyển đổi thành int. Sau đó, trong một hoạt động nhị phân, loại xếp hạng thấp hơn sẽ được chuyển đổi thành cao hơn và kết quả sẽ là loại cao hơn. (Bạn sẽ lưu ý rằng, từ cấu trúc phân cấp, bất cứ khi nào có dấu phẩy động và loại tích phân, loại tích phân sẽ được chuyển đổi thành loại dấu phẩy động.)

Unsign làm phức tạp mọi thứ một chút: nó làm xáo trộn thứ hạng và các phần của xếp hạng trở thành việc thực hiện được xác định. Bởi vì điều này, tốt nhất là không trộn lẫn chữ ký và không dấu trong cùng một biểu thức. (Hầu hết các chuyên gia C ++ dường như tránh không dấu trừ khi có liên quan đến hoạt động bitwise. Đó là, ít nhất, đó là những gì Stroustrup khuyến nghị.)


3
Stroustrup có thể đề xuất những gì anh ấy thích, nhưng sử dụng một dấu hiệu có thể intcho một số không bao giờ cần phải âm là một sự lãng phí hoàn toàn của 50% phạm vi có sẵn. Tôi chắc chắn không có Stroustrup, nhưng tôi sử dụng unsignedtheo mặc định và signedchỉ khi tôi có lý do.
gạch dưới

1
Đó là tất cả tốt và tốt, gạch dưới, cho đến ngày bạn phải trừ. Vấn đề chính với các số không dấu trong C ++ là khi bạn thực hiện phép trừ, chúng không được ký. Vì vậy, giả sử bạn viết một hàm để xem nếu một std :: vector theo thứ tự. Bạn có thể viết bool in_order(vector<T> vec) { for ( int i = 0; i < size() - 1; ++i) { if (vec[i + 1] < vec[i]) return false; } return true;và sau đó bạn sẽ thấy khó chịu khi thấy nó bị treo vì các vectơ trống vì kích thước () - 1 trả về 18446744073709551615.
jorgbrown

3

My giải pháp cho vấn đề đã WA (câu trả lời sai), sau đó tôi thay đổi một trong những intđể long long intvà nó đã cho AC (chấp nhận) . Trước đây, tôi đã cố gắng làm long long int += int * intvà sau khi tôi khắc phục nó long long int += long long int * int. Googling tôi đã đưa ra,

1. Chuyển đổi số học

Điều kiện để chuyển đổi loại:

Điều kiện đã gặp ---> Chuyển đổi

  • Hoặc toán hạng là loại dài gấp đôi . ---> Toán hạng khác được chuyển đổi thành kiểu dài gấp đôi .

  • Điều kiện trước không được đáp ứng và toán hạng là loại kép . ---> Toán hạng khác được chuyển đổi thành kiểu double .

  • Các điều kiện trước không được đáp ứng và toán hạng là kiểu float . ---> Toán hạng khác được chuyển đổi thành kiểu float .

  • Các điều kiện trước không được đáp ứng (không có toán hạng nào thuộc kiểu nổi). ---> Các chương trình khuyến mãi tích hợp được thực hiện trên các toán hạng như sau:

    • Nếu một toán hạng có kiểu không dấu dài , toán hạng khác được chuyển đổi thành kiểu không dấu dài .
    • Nếu điều kiện trước không được đáp ứng và nếu một toán hạng có kiểu dài và loại khác không dấu int thì cả hai toán hạng đều được chuyển đổi thành kiểu không dấu dài .
    • Nếu hai điều kiện trước không được đáp ứng và nếu một trong hai toán hạng có kiểu dài , thì toán hạng khác được chuyển thành loại dài .
    • Nếu ba điều kiện trước không được đáp ứng và nếu một toán hạng có kiểu không dấu int , thì toán hạng khác được chuyển đổi thành kiểu không dấu int .
    • Nếu không có điều kiện nào trước đó được đáp ứng, cả hai toán hạng đều được chuyển đổi thành kiểu int .

2. Quy tắc chuyển đổi số nguyên

  • Khuyến mãi số nguyên:

Các kiểu số nguyên nhỏ hơn int được thăng cấp khi một thao tác được thực hiện trên chúng. Nếu tất cả các giá trị của loại ban đầu có thể được biểu diễn dưới dạng int, giá trị của loại nhỏ hơn được chuyển đổi thành int; mặt khác, nó được chuyển đổi thành một số nguyên không dấu. Các khuyến mãi số nguyên được áp dụng như một phần của các chuyển đổi số học thông thường cho các biểu thức đối số nhất định; toán hạng của toán tử unary +, - và ~; và toán hạng của các toán tử thay đổi.

  • Xếp hạng chuyển đổi số nguyên:

    • Không có hai loại số nguyên đã ký sẽ có cùng thứ hạng, ngay cả khi chúng có cùng đại diện.
    • Thứ hạng của một loại số nguyên đã ký sẽ lớn hơn thứ hạng của bất kỳ loại số nguyên đã ký nào với độ chính xác thấp hơn.
    • Thứ hạng của long long intsẽ lớn hơn cấp bậc của long int, sẽ lớn hơn cấp bậc của int, sẽ lớn hơn cấp bậc của short int, sẽ lớn hơn cấp bậc của signed char.
    • Thứ hạng của bất kỳ loại số nguyên không dấu nào sẽ bằng thứ hạng của loại số nguyên đã ký tương ứng, nếu có.
    • Thứ hạng của bất kỳ loại số nguyên tiêu chuẩn nào cũng phải lớn hơn thứ hạng của bất kỳ loại số nguyên mở rộng nào có cùng chiều rộng.
    • Thứ hạng charsẽ bằng thứ hạng của signed charunsigned char.
    • Thứ hạng của bất kỳ loại số nguyên được ký mở rộng nào so với loại số nguyên được ký mở rộng khác có cùng độ chính xác được xác định theo triển khai nhưng vẫn tuân theo các quy tắc khác để xác định thứ hạng chuyển đổi số nguyên.
    • Đối với tất cả các loại số nguyên T1, T2 và T3, nếu T1 có thứ hạng lớn hơn T2 và T2 có thứ hạng lớn hơn T3, thì T1 có thứ hạng lớn hơn T3.
  • Chuyển đổi số học thông thường:

    • Nếu cả hai toán hạng có cùng loại, không cần chuyển đổi thêm.
    • Nếu cả hai toán hạng có cùng kiểu số nguyên (đã ký hoặc không dấu), toán hạng có loại xếp hạng chuyển đổi số nguyên nhỏ hơn sẽ được chuyển đổi thành loại toán hạng có thứ hạng lớn hơn.
    • Nếu toán hạng có loại số nguyên không dấu có thứ hạng lớn hơn hoặc bằng thứ hạng của loại toán hạng khác, toán hạng có loại số nguyên đã ký được chuyển đổi thành loại toán hạng có loại số nguyên không dấu.
    • Nếu loại toán hạng có loại số nguyên đã ký có thể biểu thị tất cả các giá trị của loại toán hạng có loại số nguyên không dấu, toán hạng có loại số nguyên không dấu được chuyển đổi thành loại toán hạng có loại số nguyên đã ký.
    • Mặt khác, cả hai toán hạng được chuyển đổi thành kiểu số nguyên không dấu tương ứng với loại toán hạng có kiểu số nguyên đã ký. Các hoạt động cụ thể có thể thêm hoặc sửa đổi ngữ nghĩa của các hoạt động số học thông thường.

1

Toàn bộ chương 4 nói về chuyển đổi, nhưng tôi nghĩ bạn nên quan tâm nhất đến những điều này:

4.5 Các khuyến mãi tích hợp [conv.prom]
Một giá trị của kiểu char, char đã ký, char không dấu, int ngắn hoặc int unsign ngắn có thể được chuyển đổi thành một giá trị của kiểu int nếu int có thể biểu thị tất cả các giá trị của loại nguồn; Mặt khác
, giá trị nguồn có thể được chuyển đổi thành giá trị loại không dấu int.
Một giá trị của loại wchar_t (3.9.1) hoặc loại liệt kê (7.2) có thể được chuyển đổi thành một
giá trị của loại đầu tiên sau đây có thể đại diện cho tất cả các giá trị của loại cơ bản của nó: int, unsign int,
long hoặc unsign Dài.
Một giá trị cho trường bit tích phân (9.6) có thể được chuyển đổi thành
giá trị của kiểu int nếu int có thể biểu thị tất cả các giá trị của trường bit; mặt khác, nó có thể được chuyển đổi thành int unsign nếu int unsign có thể rep-
bực bội tất cả các giá trị của trường bit. Nếu trường bit lớn hơn, không áp dụng khuyến mãi tích hợp cho nó. Nếu trường
bit có loại liệt kê, nó được coi là bất kỳ giá trị nào khác của loại đó cho mục đích quảng cáo.
Một giá trị của kiểu bool có thể được chuyển đổi thành một giá trị của kiểu int, với sai trở thành 0 và đúng
trở thành một.
Những chuyển đổi này được gọi là khuyến mãi không thể thiếu.

4.6 Quảng cáo điểm nổi [conv.fpprom]
Một giá trị của kiểu float có thể được chuyển đổi thành một giá trị của kiểu double. Giá trị không đổi.
Chuyển đổi này được gọi là khuyến mãi điểm nổi.

Do đó, tất cả các chuyển đổi liên quan đến float - kết quả là float.

Chỉ có một liên quan đến cả int - kết quả là int: int / int = int


1

Loại biểu thức, khi không phải cả hai phần cùng loại, sẽ được chuyển đổi thành phần lớn nhất của cả hai. Vấn đề ở đây là để hiểu cái nào lớn hơn cái kia (nó không liên quan gì đến kích thước tính theo byte).

Trong các biểu thức có liên quan đến số thực và số nguyên, số nguyên sẽ được thăng cấp thành số thực. Ví dụ, trong int + float, loại biểu thức là float.

Sự khác biệt khác có liên quan đến khả năng của loại. Ví dụ, một biểu thức liên quan đến một int và một int dài sẽ là kết quả của kiểu int dài.


2
Đây không phải là sự thật. Trên nền tảng có thể a long"lớn" hơn a floatnhưng loại long+ là floatgì?
CB Bailey

1
-1: Ý của bạn là gì lớn nhất ? Là một float lớn hơn một int? Hay ngược lại ?
Paul R

2
Cảm ơn bạn đã bình luận của bạn. Yep kích thước tính theo byte ở đây không đáng quan tâm chút nào. Khi nó xuất hiện, rõ ràng đặt chữ in nghiêng lớn nhất là không đủ để giải thích câu trả lời. Dù sao, không có nghĩa gì để giải thích nó sâu sắc hơn, vì bây giờ có những câu trả lời khác, rất kỹ lưỡng.
Baltasarq

-2

Hãy cẩn thận!

Các chuyển đổi xảy ra từ trái sang phải.

Thử cái này:

int i = 3, j = 2;
double k = 33;
cout << k * j / i << endl; // prints 22
cout << j / i * k << endl; // prints 0

8
Đó không phải là vì chuyển đổi mà là vì sự ưu tiên của nhà điều hành. j + i * ksẽ có kết quả vào năm 101.
gartriese
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.