Aturan konversi jenis implisit di operator C ++

167

Saya ingin menjadi lebih baik tentang mengetahui kapan saya harus berperan. Apa aturan konversi tipe implisit dalam C ++ saat menambahkan, mengalikan, dll. Misalnya,

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

dan lain-lain ...

Akankah ekspresi selalu dievaluasi sebagai tipe yang lebih tepat? Apakah aturannya berbeda untuk Java? Harap perbaiki saya jika saya telah menjawab pertanyaan ini dengan tidak akurat.

Matt Montag
sumber
16
Yang perlu diingat ^adalah XOR.
GManNickG
16
@int ^ float = compile error :)
Serge Dundich

Jawaban:

223

Dalam operator C ++ (untuk tipe POD) selalu bertindak pada objek dengan tipe yang sama.
Jadi jika mereka tidak sama satu akan dipromosikan untuk mencocokkan yang lain.
Jenis hasil operasi sama dengan operan (setelah konversi).

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

Catatan. Ukuran minimum operasi adalah int. Jadi short/ chardipromosikan untuk intsebelum operasi dilakukan.

Dalam semua ekspresi Anda, intdipromosikan ke floatsebelum operasi dilakukan. Hasil operasi adalah 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>
Martin York
sumber
1
"Ukuran minimum operasi adalah int." - Ini akan sangat aneh (bagaimana dengan arsitektur yang secara efisien mendukung operasi char / short?) Apakah ini benar-benar dalam spesifikasi C ++?
Rafał Dowgird
3
@ Rafal: Ya. int seharusnya menjadi tipe integer yang paling efisien untuk operasi pada platform tertentu. char harus selalu 1 tetapi pendek bisa dengan ukuran yang sama dengan int.
Martin York
1
@ Rafał: ya, ini sangat aneh dan masih dalam standar. Dalam banyak kasus, arsitektur yang Anda gambarkan dapat menggunakan jenisnya yang sangat efisien char. Jika nilai char + charditugaskan ke char, maka itu hanya dapat melakukan aritmatika chardan misalnya membungkus. Tetapi jika hasilnya ditugaskan intmaka harus melakukan aritmatika dalam jenis yang cukup besar untuk mendapatkan hasil yang benar ketika lebih dari CHAR_MAX.
Steve Jessop
2
Saya hanya ingin menekankan fakta bahwa int dipromosikan menjadi int unsigned !!! Saya telah berjuang dengan bug selama berhari-hari karena saya mendapat kesan bahwa keduanya akan dipromosikan ke int atau panjang sehingga hasil negatif yang mungkin tidak akan menyebabkan underflow / membungkus.
nitsas
10
Contoh masalah " int dipromosikan menjadi unsigned int ": ((int) 4) - ((unsigned int) 5)akan menghasilkan 4294967295int 32 bit dan 32 bit int unsigned.
nitsas
33

Operasi aritmatika yang melibatkan floathasil dalam float.

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

Untuk jawaban lebih detail. Lihatlah apa yang dikatakan bagian §5 / 9 dari Standar C ++

Banyak operator biner yang mengharapkan operan tipe aritmatika atau enumerasi menyebabkan konversi dan menghasilkan tipe hasil dengan cara yang sama. Tujuannya adalah untuk menghasilkan jenis yang umum, yang juga merupakan jenis hasilnya .

Pola ini disebut konversi aritmatika biasa, yang didefinisikan sebagai berikut:

- Jika salah satu operan bertipe long double, yang lain harus dikonversi menjadi long double.

- Kalau tidak, jika salah satu operan ganda, yang lain harus dikonversi menjadi ganda.

- Kalau tidak, jika salah satu operan mengambang, yang lain harus dikonversi menjadi mengambang.

- Kalau tidak, promosi integral (4.5) harus dilakukan pada kedua operan.54)

- Kemudian, jika salah satu operan tidak ditandatangani lama yang lain akan dikonversi menjadi tidak ditandatangani lama.

- Jika tidak, jika satu operan adalah int panjang dan int unsigned lainnya, maka jika int panjang dapat mewakili semua nilai dari int unsigned, int unsigned harus dikonversi ke int panjang; jika tidak kedua operan akan dikonversi menjadi int panjang yang tidak ditandatangani.

- Kalau tidak, jika salah satu operan panjang, yang lain harus dikonversi menjadi panjang.

- Kalau tidak, jika salah satu operan tidak ditandatangani, yang lain akan dikonversi menjadi unsigned.

[Catatan: jika tidak, satu-satunya kasing yang tersisa adalah bahwa kedua operan adalah int]

Nawaz
sumber
3
... selama jenis lainnya bukan doubleatau tidak long double.
CB Bailey
1
@ Charles: Benar. Saya mengutip bagian yang relevan dari Standar untuk mengklarifikasi lebih lanjut.
Nawaz
Jadi bisakah integer selalu dikonversi menjadi float tanpa kehilangan data? (mis. dengan mem-nolkan eksponen dan menggunakan segalanya untuk mantissa)?
Marco A.
1
Jawaban ini kedaluwarsa. Sarankan pembaruan. Khususnya, long longdan unsigned longtidak dibahas di sini.
chux - Reinstate Monica
@MarcoA. 32-bit floattidak memiliki cukup bit dalam mantissa (24 bit untuk IEEE-754 ) untuk 32-bit int, jadi mungkin ada beberapa kehilangan data. A 64-bit doubleseharusnya baik-baik saja.
Mark Ransom
17

Karena jawaban lain jangan bicara tentang aturan di C ++ 11, inilah salah satunya. Dari standar C ++ 11 (draft n3337) §5 / 9 (menekankan perbedaan):

Pola ini disebut konversi aritmatika biasa , yang didefinisikan sebagai berikut:

- Jika salah satu operan adalah jenis pencacahan cakupan, tidak ada konversi yang dilakukan; jika operan lain tidak memiliki tipe yang sama, ekspresi tersebut terbentuk buruk.

- Jika salah satu operan bertipe long double, yang lain harus dikonversi menjadi long double.

- Kalau tidak, jika salah satu operan ganda, yang lain harus dikonversi menjadi ganda.

- Kalau tidak, jika salah satu operan mengambang, yang lain harus dikonversi menjadi mengambang.

- Kalau tidak, promosi integral harus dilakukan pada kedua operan. Kemudian aturan berikut harus diterapkan pada operan yang dipromosikan:

- Jika kedua operan memiliki tipe yang sama, tidak diperlukan konversi lebih lanjut.

- Jika tidak, jika kedua operan memiliki tipe integer yang ditandatangani atau keduanya memiliki tipe integer yang tidak ditandatangani, operand dengan jenis peringkat konversi integer yang lebih rendah harus dikonversi ke jenis operan dengan peringkat yang lebih besar.

- Kalau tidak, jika operan yang memiliki tipe bilangan bulat bertanda memiliki peringkat lebih besar dari atau sama dengan pangkat jenis operan lainnya, operan dengan tipe bilangan bulat yang ditandatangani harus dikonversi ke jenis operan dengan jenis bilangan bulat bertanda.

- Jika tidak, jika jenis operan dengan tipe integer yang ditandatangani dapat mewakili semua nilai dari tipe operan dengan tipe integer yang tidak ditandatangani, operan dengan tipe integer yang tidak ditandatangani harus dikonversi ke jenis operan dengan tipe integer yang ditandatangani.

- Jika tidak, kedua operan harus dikonversi ke tipe integer yang tidak bertanda yang sesuai dengan jenis operan dengan tipe integer yang ditandatangani.

Lihat di sini untuk daftar yang sering diperbarui.

legends2k
sumber
1
Aturan-aturan ini sama di semua versi C ++, kecuali untuk pencacahan scoped yang ditambahkan dalam C ++ 11 tentu saja
MM
6

Jawaban ini sebagian besar diarahkan pada komentar yang dibuat oleh @ RafałDowgird:

"Ukuran minimum operasi adalah int." - Ini akan sangat aneh (bagaimana dengan arsitektur yang secara efisien mendukung operasi char / short?) Apakah ini benar-benar dalam spesifikasi C ++?

Perlu diingat bahwa standar C ++ memiliki aturan "as-jika" yang sangat penting. Lihat bagian 1.8: Eksekusi Program:

3) Ketentuan ini kadang-kadang disebut aturan "as-if", karena suatu implementasi bebas untuk mengabaikan persyaratan apa pun dari Standar selama hasilnya adalah seolah-olah persyaratan tersebut telah dipatuhi, sejauh dapat ditentukan dari yang dapat diamati. perilaku program.

Kompiler tidak dapat menetapkan intukuran 8 bit, bahkan jika itu yang tercepat, karena standar mengamanatkan minimum 16-bit int.

Oleh karena itu, dalam kasus komputer teoretis dengan operasi 8-bit super cepat, promosi implisit ke intuntuk aritmatika bisa menjadi masalah. Namun, untuk banyak operasi, Anda tidak dapat mengetahui apakah kompiler benar-benar melakukan operasi dalam ketepatan intdan kemudian dikonversi menjadi charuntuk menyimpan dalam variabel Anda, atau jika operasi dilakukan di char selama ini.

Misalnya, pertimbangkan unsigned char = unsigned char + unsigned char + unsigned char, di mana penambahan akan meluap (mari kita asumsikan nilai 200 untuk masing-masing). Jika Anda dipromosikan ke int, Anda akan mendapatkan 600, yang kemudian secara implisit akan dilemparkan ke dalam unsigned char, yang akan membungkus modulo 256, sehingga memberikan hasil akhir dari 88. Jika Anda tidak melakukan promosi seperti itu, Anda harus membungkus antara yang pertama dua tambahan, yang akan mengurangi masalah dari 200 + 200 + 200ke 144 + 200, yaitu 344, yang berkurang menjadi 88. Dengan kata lain, program tidak mengetahui perbedaannya, sehingga kompiler bebas untuk mengabaikan mandat untuk melakukan operasi perantara intjika operan memiliki peringkat lebih rendah dari int.

Ini berlaku secara umum penambahan, pengurangan, dan multiplikasi. Itu tidak benar secara umum untuk pembagian atau modulus.

David Stone
sumber
4

Jika Anda mengecualikan tipe yang tidak ditandatangani, ada hierarki yang diurutkan: char yang ditandatangani, pendek, int, panjang, panjang, float, ganda, panjang ganda. Pertama, apa pun yang datang sebelum int di atas akan dikonversi menjadi int. Kemudian, dalam operasi biner, tipe peringkat yang lebih rendah akan dikonversi ke yang lebih tinggi, dan hasilnya akan menjadi tipe yang lebih tinggi. (Anda akan mencatat bahwa, dari hierarki, kapan saja titik mengambang dan tipe integral terlibat, tipe integral akan dikonversi ke tipe titik mengambang.)

Unsigned merumitkan hal-hal sedikit: itu mengganggu peringkat, dan bagian-bagian dari peringkat menjadi implementasi yang ditetapkan. Karena itu, yang terbaik adalah tidak mencampur ditandatangani dan tidak ditandatangani dalam ekspresi yang sama. (Kebanyakan ahli C ++ tampaknya menghindari unsigned kecuali jika operasi bitwise terlibat. Itulah, setidaknya, apa yang direkomendasikan Stroustrup.)

James Kanze
sumber
3
Stroustrup dapat merekomendasikan apa yang dia suka, tetapi menggunakan tanda intuntuk nomor yang tidak perlu negatif adalah pemborosan total hingga 50% dari kisaran yang tersedia. Saya tentu saja tidak Stroustrup, tetapi saya menggunakan unsignedsecara default dan signedhanya ketika saya punya alasan.
underscore_d
1
Itu semua baik dan bagus, underscore_d, sampai hari ketika Anda harus mengurangi. Masalah utama dengan angka yang tidak ditandatangani di C ++ adalah bahwa ketika Anda melakukan pengurangan, mereka tetap tidak ditandatangani. Jadi misalkan Anda menulis fungsi untuk melihat apakah std :: vector dalam urutan. Anda dapat menulis bool in_order(vector<T> vec) { for ( int i = 0; i < size() - 1; ++i) { if (vec[i + 1] < vec[i]) return false; } return true;dan kemudian Anda akan merasa terganggu untuk menemukan bahwa itu crash untuk vektor kosong karena ukuran () - 1 mengembalikan 18446744073709551615.
jorgbrown
3

Saya solusi untuk masalah mendapat WA (jawaban yang salah), maka saya mengubah salah satu intuntuk long long intdan itu memberi AC (menerima) . Sebelumnya, saya coba lakukan long long int += int * int, dan setelah saya perbaiki long long int += long long int * int. Googling saya datang dengan,

1. Konversi Aritmatika

Ketentuan untuk Konversi Jenis:

Ketentuan Bertemu ---> Konversi

  • Salah satu dari operan adalah tipe double panjang . ---> Operand lainnya dikonversi ke tipe double panjang .

  • Kondisi sebelumnya tidak terpenuhi dan kedua operan bertipe double . ---> Operand lainnya dikonversi menjadi tipe double .

  • Kondisi sebelumnya tidak terpenuhi dan salah satu operan adalah tipe float . ---> Operand lainnya dikonversi ke tipe float .

  • Kondisi sebelumnya tidak terpenuhi (tidak ada operan yang tipe mengambang). ---> Promosi integral dilakukan pada operan sebagai berikut:

    • Jika salah satu operan bertipe unsigned long , operan lainnya dikonversi menjadi mengetik unsigned long .
    • Jika kondisi sebelumnya tidak terpenuhi, dan jika salah satu operan bertipe panjang dan yang lainnya bertipe int tidak bertanda , kedua operan dikonversi menjadi jenis yang tidak bertanda panjang .
    • Jika dua kondisi sebelumnya tidak terpenuhi, dan jika salah satu operan bertipe panjang , maka operan lainnya dikonversi menjadi tipe panjang .
    • Jika tiga kondisi sebelumnya tidak terpenuhi, dan jika salah satu operan bertipe int unsigned , operan lainnya akan dikonversi ke jenis unsigned int .
    • Jika tidak ada kondisi sebelumnya yang terpenuhi, kedua operan dikonversi menjadi tipe int .

2. Aturan konversi bilangan bulat

  • Promosi Integer:

Jenis integer yang lebih kecil dari int dipromosikan ketika operasi dilakukan pada mereka. Jika semua nilai tipe asli dapat direpresentasikan sebagai int, nilai tipe yang lebih kecil dikonversi ke int; jika tidak, itu dikonversi ke int yang tidak ditandatangani. Promosi integer diterapkan sebagai bagian dari konversi aritmatika biasa ke ekspresi argumen tertentu; operan dari operator +, -, dan ~ unary; dan operan dari operator shift.

  • Peringkat Konversi Integer:

    • Tidak ada dua tipe integer yang ditandatangani memiliki peringkat yang sama, bahkan jika mereka memiliki representasi yang sama.
    • Peringkat tipe integer yang ditandatangani harus lebih besar dari peringkat tipe integer yang ditandatangani dengan ketelitian yang lebih rendah.
    • Pangkat long long intakan lebih besar dari pangkat long int, yang akan lebih besar dari pangkat int, yang akan lebih besar dari pangkat short int, yang akan lebih besar dari pangkat signed char.
    • Peringkat dari tipe integer yang tidak ditandatangani harus sama dengan peringkat dari tipe integer yang ditandatangani, jika ada.
    • Peringkat dari setiap tipe integer standar harus lebih besar dari peringkat dari tipe integer yang diperluas dengan lebar yang sama.
    • Pangkat charharus sama dengan pangkat signed chardan unsigned char.
    • Peringkat dari setiap jenis bilangan bulat bertanda yang diperpanjang relatif terhadap jenis bilangan bulat bertanda yang diperpanjang lainnya dengan presisi yang sama ditentukan oleh implementasi tetapi masih tunduk pada aturan lain untuk menentukan peringkat konversi bilangan bulat.
    • Untuk semua tipe integer T1, T2, dan T3, jika T1 memiliki peringkat lebih besar dari T2 dan T2 memiliki peringkat lebih besar dari T3, maka T1 memiliki peringkat lebih besar dari T3.
  • Konversi Aritmatika Biasa:

    • Jika kedua operan memiliki tipe yang sama, tidak perlu konversi lebih lanjut.
    • Jika kedua operan memiliki tipe bilangan bulat yang sama (ditandatangani atau tidak ditandatangani), operan dengan jenis peringkat konversi integer yang lebih rendah dikonversi ke jenis operan dengan peringkat yang lebih besar.
    • Jika operan yang memiliki tipe bilangan bulat bertanda memiliki peringkat lebih besar dari atau sama dengan pangkat dari jenis operan lain, operan dengan tipe bilangan bulat bertanda dikonversi ke jenis operan dengan jenis bilangan bulat bertanda.
    • Jika tipe operan dengan tipe integer yang ditandatangani dapat mewakili semua nilai dari tipe operan dengan tipe integer yang tidak ditandatangani, operan dengan tipe integer yang tidak ditandatangani dikonversi ke tipe operan dengan tipe integer yang ditandatangani.
    • Jika tidak, kedua operan dikonversikan ke tipe integer yang tidak ditandatangani yang sesuai dengan tipe operan dengan tipe integer yang ditandatangani. Operasi khusus dapat menambah atau mengubah semantik dari operasi aritmatika yang biasa.
garakchy
sumber
1

Bab 4 seluruh berbicara tentang konversi, tetapi saya pikir Anda harus paling tertarik pada ini:

4.5 Promosi integral [conv.prom] Nilai
dari tipe char, char yang ditandatangani, char yang tidak ditandatangani, int yang pendek, atau unsigned yang dapat dikonversi menjadi nilai dari tipe int jika int dapat mewakili semua nilai dari tipe sumber; selain itu
, nilai sumber dapat dikonversi ke nilai jenis int unsigned.
Nilai dari tipe wchar_t (3.9.1) atau tipe enumeration (7.2) dapat dikonversi ke nilai pertama
dari tipe berikut yang dapat mewakili semua nilai dari tipe yang mendasarinya: int, int unsigned int,
long, atau unsigned panjang.
Nilai untuk bidang bit integral (9,6) dapat dikonversi ke nilai jenis int jika int dapat mewakili semua
nilai bidang bit; jika tidak, dapat dikonversi ke int yang tidak ditandatangani jika int yang tidak ditandatangani dapat
kirim ulang semua nilai bit-field. Jika bit-field lebih besar, tidak ada promosi integral yang berlaku. Jika
bidang-bit memiliki tipe yang disebutkan, itu diperlakukan sebagai nilai lain dari tipe itu untuk tujuan promosi.
Nilai dari tipe bool dapat dikonversi menjadi nilai dari tipe int, dengan false menjadi nol dan benar
menjadi satu.
Konversi ini disebut promosi integral.

4,6 Floating point promotion [conv.fpprom] Nilai
dari tipe float dapat dikonversi menjadi nilai tipe double. Nilai tidak berubah.
Konversi ini disebut promosi floating point.

Karenanya, semua konversi yang melibatkan float - hasilnya adalah float.

Hanya yang melibatkan kedua int - hasilnya adalah int: int / int = int

BЈовић
sumber
1

Jenis ekspresi, ketika tidak kedua bagian dari jenis yang sama, akan dikonversi menjadi yang terbesar dari keduanya. Masalahnya di sini adalah untuk memahami mana yang lebih besar dari yang lain (tidak ada hubungannya dengan ukuran dalam byte).

Dalam ekspresi yang melibatkan bilangan real dan bilangan bulat, bilangan bulat akan dipromosikan ke bilangan real. Misalnya, dalam int + float, tipe ekspresi adalah float.

Perbedaan lainnya terkait dengan kemampuan tipe. Misalnya, ekspresi yang melibatkan int dan int panjang akan menghasilkan tipe long int.

Baltasarq
sumber
2
Ini tidak benar. Pada Mei platform a long"lebih besar" dari a floattetapi apa jenis long+ float?
CB Bailey
1
-1: Apa yang Anda maksud dengan terbesar ? Apakah float lebih besar dari int? Atau sebaliknya ?
Paul R
2
Terima kasih atas komentar anda Yap ukuran dalam byte di sini tidak menarik sama sekali. Ketika keluar, jelas menempatkan terbesar dalam huruf miring tidak cukup untuk menjelaskan jawabannya. Bagaimanapun, tidak masuk akal untuk menjelaskannya lebih dalam, karena sekarang ada jawaban lain yang sangat menyeluruh.
Baltasarq
-2

Peringatan!

Konversi terjadi dari kiri ke kanan.

Coba ini:

int i = 3, j = 2;
double k = 33;
cout << k * j / i << endl; // prints 22
cout << j / i * k << endl; // prints 0
Habib
sumber
9
Itu bukan karena konversi tetapi karena prioritas operator. j + i * kakan menghasilkan 101.
gartenriese