C ++ işleçlerinde örtük tür dönüştürme kuralları


168

Ne zaman yayın yapacağımı bilmek konusunda daha iyi olmak istiyorum. Ekleme, çarpma, vb. Yaparken C ++ 'da örtük tür dönüştürme kuralları nelerdir? Örneğin,

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

ve benzeri...

İfade her zaman daha kesin tip olarak değerlendirilecek mi? Java için kurallar farklı mı? Bu soruyu yanlış ifade ettiysem lütfen beni düzeltin.


16
Unutmayın ^XOR.
GManNickG

16
@int ^ float = derleme hatası :)
Serge Dundich

Yanıtlar:


223

C ++ 'da işleçler (POD türleri için) her zaman aynı türdeki nesneler üzerinde çalışır.
Böylece aynı değillerse, diğeriyle eşleşecek şekilde terfi ettirilecektir.
İşlem sonucunun türü, işlenenlerle aynıdır (dönüşümden sonra).

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

Not. Minimum işlem boyutu int. Yani short/ işlem yapılmadan önce charterfi edilir int.

Tüm ifadelerinizde , işlem gerçekleştirilmeden önce inta değeri yükseltilir float. Operasyonun sonucu 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
"Minimum işlem boyutu int." - Bu çok garip olurdu (karakter / kısa işlemleri etkin bir şekilde destekleyen mimariler ne olacak?) Bu gerçekten C ++ spesifikasyonunda mı?
Rafał Dowgird

3
@Rafal: Evet. int, belirli bir platformda çalışmak için en verimli tamsayı türü olarak kabul edilir. char her zaman 1 olmalıdır, ancak kısa int ile aynı boyutta olabilir.
Martin York

1
@ Rafał: evet, çok garip ve standart. Çoğu durumda, tanımladığınız mimari süper verimli chartipini kullanabilir. Değeri a değerine char + charatanırsa char, yalnızca aritmetiği yapabilir charve örneğin etrafına sarılabilir. Ancak sonuç atanırsa, intdaha fazla olduğunda doğru sonucu elde etmek için yeterince büyük bir türde aritmetik yapmak zorundadır CHAR_MAX.
Steve Jessop

2
Ben sadece int imzasız int terfi olduğunu vurgulamak istiyorum !!! Günlerce hatalarla mücadele ettim çünkü her ikisinin de int veya long'a yükseltileceği izlenimi altındaydım, böylece olası bir negatif sonuç bir alt / sargıya neden olmaz.
nitsas

10
" İnt imzasız int olarak yükseltilir " sorununa örnek : 32 bit ints ve 32 bit imzasız ints ile ((int) 4) - ((unsigned int) 5)sonuçlanır 4294967295.
nitsas

33

floatSonuçları içeren aritmetik işlemler float.

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

Daha ayrıntılı cevap için. C ++ Standardının §5 / 9 bölümünün ne dediğine bakın

Aritmetik veya numaralandırma tipi işlenenleri bekleyen birçok ikili operatör, benzer şekilde dönüşümlere ve sonuç sonuç türlerine neden olur. Amaç, aynı zamanda sonucun türü olan ortak bir tür vermektir .

Bu desene, aşağıdaki gibi tanımlanan olağan aritmetik dönüşümler denir:

- İşlenenlerden biri uzun iki kat ise, diğeri uzun iki katına dönüştürülecektir.

- Aksi takdirde, işlenenlerden biri çift ise, diğeri ikiye dönüştürülecektir.

- Aksi takdirde, eğer işlenenlerden biri şamandıra ise, diğeri şamandıraya dönüştürülecektir.

- Aksi takdirde, integral tanıtımlar (4.5) her iki işlenende de gerçekleştirilecektir.

- Daha sonra, eğer işlenenlerden biri uzun imzalanmamışsa, diğeri imzalanmamış uzunluğa dönüştürülecektir.

- Aksi takdirde, bir işlenen bir uzun int ve diğer işaretsiz int ise, o zaman uzun bir int işaretsiz bir int'in tüm değerlerini temsil edebiliyorsa, işaretsiz int uzun int'e dönüştürülecektir; aksi takdirde her iki işlenen imzasız uzun int'ye dönüştürülecektir.

- Aksi takdirde, eğer işlenenlerden biri uzunsa, diğeri uzun olana dönüştürülecektir.

- Aksi takdirde, herhangi bir işlenen imzasızsa, diğeri imzasıza dönüştürülecektir.

[Not: aksi takdirde kalan tek durum her iki işlenenin de int olmasıdır]


3
... bu kadar uzun diğer tip ne olduğu gibi doublene de long double.
CB Bailey

1
@Charles: Doğru. Daha fazla açıklığa kavuşturmak için Standarttan ilgili bölümü alıntıladım.
Nawaz

Öyleyse bir tamsayı her zaman veri kaybı olmadan float'a dönüştürülebilir mi? (örneğin üssü sıfırlayarak ve mantis için her şeyi kullanarak)?
Marco

1
Bu cevap güncel değil. Güncelleme öner. Özellikle, long longve unsigned longburada ele alınmamıştır.
chux - Monica'yı iade et

@MarcoA. 32 bitlik bir floatmantis yeterli bit (24 bit yok IEEE-754 bir 32-bit) int, bu nedenle bir miktar veri kaybı olabilir. Bir 64-bit iyi doubleolmalı.
Mark Ransom

17

Diğer cevaplar C ++ 11'deki kurallardan bahsetmediği için burada bir tane var. C ++ 11 standardından (taslak n3337) §5 / 9 (farkı vurguladı):

Bu desene, aşağıdaki gibi tanımlanan olağan aritmetik dönüşümler denir :

- Her iki işlenen de kapsamlı numaralandırma türündeyse, dönüştürme yapılmaz; diğer işlenen aynı tipte değilse, ifade yanlış biçimlendirilir.

- İşlenenlerden biri uzun iki kat ise, diğeri uzun iki katına dönüştürülecektir.

- Aksi takdirde, işlenenlerden biri çift ise, diğeri ikiye dönüştürülecektir.

- Aksi takdirde, eğer işlenenlerden biri şamandıra ise, diğeri şamandıraya dönüştürülecektir.

- Aksi takdirde, integral tanıtımlar her iki işlenende de gerçekleştirilecektir. Daha sonra terfi ettirilen işlenenlere aşağıdaki kurallar uygulanacaktır:

- Her iki işlenen de aynı türe sahipse, başka dönüştürmeye gerek yoktur.

- Aksi takdirde, her iki işlenen de işaretli tamsayı tipine sahipse veya her ikisi de işaretsiz tamsayı tipine sahipse, daha az tamsayı dönüştürme sıralaması türüne sahip işlenen, daha büyük bir sıraya sahip işlenen türüne dönüştürülecektir.

- Aksi takdirde, işaretsiz tamsayı türüne sahip işlenen diğer işlenen türünün sıralamasından daha büyük veya ona eşitse, işaretli tamsayı türüne sahip işlenen, işaretsiz tamsayı türüne sahip işlenen türüne dönüştürülecektir.

- Aksi takdirde, işaretli tamsayı tipine sahip işlenen türü, işaretsiz tamsayı tipine sahip işlenen tipinin tüm değerlerini temsil edebiliyorsa, işaretsiz tamsayı tipine sahip işlenen, işaretli tamsayı tipine sahip işlenen tipine dönüştürülecektir.

- Aksi takdirde, her iki işlenen de işaretli tamsayı tipine sahip işlenen tipine karşılık gelen imzasız tamsayı tipine dönüştürülecektir.

Sık güncellenen bir liste için buraya bakın .


1
Elbette C ++ 11'e eklenen kapsamlı numaralandırmalar hariç, bu kurallar C ++ 'ın tüm sürümlerinde aynıydı
MM

6

Bu cevap büyük ölçüde @ RafałDowgird tarafından yapılan bir yorumda yönlendirilmiştir:

"Minimum işlem boyutu int." - Bu çok garip olurdu (karakter / kısa işlemleri etkin bir şekilde destekleyen mimariler ne olacak?) Bu gerçekten C ++ spesifikasyonunda mı?

C ++ standardının tüm önemli "as-if" kuralına sahip olduğunu unutmayın. Bkz. Bölüm 1.8: Program Yürütme:

3) Bu hükme bazen "olduğu gibi" kuralı denir, çünkü bir uygulama, gözlemlenebilirden tespit edilebildiği sürece, sonucun yerine getirilmiş olması koşuluyla, Standardın herhangi bir gereksinimini göz ardı etmekte serbesttir. program davranışı.

Derleyici int, standart en az 16 bit gerektirdiğinden, en hızlı olsa bile a'yı 8 bit olarak ayarlayamaz int.

Bu nedenle, süper hızlı 8-bit işlemlere sahip teorik bir bilgisayar söz konusu olduğunda, intaritmetik için örtülü tanıtım önemli olabilir. Ancak, birçok işlem için, derleyicinin işlemleri gerçekten bir hassasiyetinde yapıp yapmadığını intve sonra chardeğişkeninizde depolamaya dönüştürülüp dönüştürülmediğini veya işlemlerin karakter boyunca tamamlanıp tamamlanmadığını söyleyemezsiniz .

Örneğin, unsigned char = unsigned char + unsigned char + unsigned chareklemenin nereye taşacağını düşünün (her biri için 200 değerini varsayalım). Eğer terfi intettiyseniz, o zaman unsigned charmodulo 256'yı saran ve böylece 88'in nihai sonucunu veren bir dolaylı olarak dökülecek olan 600 elde edersiniz. Böyle bir promosyon yapmadıysanız, ilk promosyon arasında problemi azaltacak iki ekleme, 200 + 200 + 200için 144 + 200derleyici içinde bir ara madde işlemleri gerçekleştirmek için görev göz ardı sağlayacak şekilde, diğer bir deyişle 88 azaltır 344 olan, program, farkı bilmiyor intişlenen varsa daha düşük bir sıralama int.

Bu genel olarak toplama, çıkarma ve çarpma için geçerlidir. Genel olarak bölünme veya modül için doğru değildir.


4

İmzasız türleri hariç tutarsanız, sıralı bir hiyerarşi vardır: imzalı karakter, kısa, int, uzun, uzun uzun, kayan, çift, uzun çift. İlk olarak, yukarıda int'ten önce gelen her şey int'ye dönüştürülecektir. Daha sonra, bir ikili işlemde, daha düşük sıralı tür daha yüksek olana dönüştürülür ve sonuçlar daha yüksek olanın türü olacaktır. (Hiyerarşiden bir kayan nokta ve bir integral türü söz konusu olduğunda, integral türünün kayan nokta türüne dönüştürüleceğini unutmayın.)

İmzasızlar işleri biraz karmaşıklaştırır: sıralamayı bozar ve sıralamanın bazı bölümleri uygulama olarak tanımlanır. Bu nedenle, aynı ifadede imzalı ve imzasız olanları karıştırmamak en iyisidir. (Çoğu C ++ uzmanı, bitsel işlemler yapılmadıkça imzasız görünmüyor gibi görünüyor. En azından Stroustrup'un önerdiği şey bu.)


3
Stroustrup, sevdiği şeyi önerebilir, ancak intasla negatif olması gerekmeyen bir sayı için bir işaret kullanmak , mevcut aralığın tam% 50'sinin tam bir israfıdır. Ben kesinlikle Stroustrup değilim, ama unsignedvarsayılan olarak ve signedsadece bir nedenim olduğunda kullanıyorum.
underscore_d

1
Her şey iyi ve güzel, underscore_d, çıkarmanız gereken güne kadar. C ++ 'da imzasız sayılarla ilgili birincil sorun, çıkartma yaptığınızda imzasız kalmalarıdır. Bir std :: vector'un sırada olup olmadığını görmek için bir işlev yazdığınızı varsayalım. Sen yazabiliriz bool in_order(vector<T> vec) { for ( int i = 0; i < size() - 1; ++i) { if (vec[i + 1] < vec[i]) return false; } return true;boş vektörler için çöküyor bulmak için o zaman rahatsız olurdum çünkü boyutu () - 18446744073709551615. 1 döner
jorgbrown

3

Benim çözüm için sorunun WA (yanlış cevap), sonra ben birini değiştirmiş var intetmek long long intve onu verdi AC (kabul) . Daha önce, yapmaya çalışıyordum long long int += int * intve düzelttikten sonra long long int += long long int * int. Google ile geldim,

1. Aritmetik Dönüşümler

Tür Dönüştürme Koşulları:

Karşılaşılan Koşullar ---> Dönüşüm

  • Her iki işlenen de long double tipindedir . ---> Diğer işlenenler, long double türüne dönüştürülür .

  • Önceki koşul karşılanmadı ve her iki işlenen de çift tiptedir . ---> Diğer işlenenler double tipine dönüştürülür .

  • Önceki koşullar karşılanmadı ve her iki işlenen de float tipindedir . ---> Diğer işlenen şamandıra türüne dönüştürülür .

  • Önceki koşullar karşılanmadı (işlenenlerin hiçbiri değişken tipte değil). ---> İşlenenlerde integral promosyonları aşağıdaki gibi yapılır:

    • Her iki işlenen imzasız uzun tipteyse, diğer işlenen imzasız uzun tipe dönüştürülür .
    • Önceki koşul karşılanmazsa ve her iki işlenen de uzun ve diğeri işaretsiz int türündeyse , her iki işlenen de işaretsiz uzun türe dönüştürülür .
    • Önceki iki koşul karşılanmazsa ve her iki işlenen de uzun tipse, diğer işlenen uzun tipe dönüştürülür .
    • Önceki üç koşul karşılanmazsa ve her iki işlenen de imzasız int türündeyse , diğer işlenen imzasız int türüne dönüştürülür .
    • Yukarıdaki koşulların hiçbiri karşılanmazsa, her iki işlenen de int türüne dönüştürülür .

2. Tamsayı dönüşüm kuralları

  • Tam Sayı Promosyonlar:

Üzerinde bir işlem yapıldığında int'ten daha küçük tamsayı türleri tanıtılır. Orijinal türün tüm değerleri int olarak gösterilebiliyorsa, küçük türün değeri int değerine dönüştürülür; aksi takdirde, imzasız bir int'e dönüştürülür. Tamsayı yükseltmeleri, belirli argüman ifadelerine olağan aritmetik dönüşümlerin bir parçası olarak uygulanır; tekli +, - ve ~ operatörlerinin işlenenleri; ve vardiya operatörlerinin işlenenleri.

  • Tamsayı Dönüşüm Sıralaması:

    • Aynı temsile sahip olsalar bile, imzalı iki tamsayı türü aynı sıralamaya sahip olamaz.
    • İşaretli bir tam sayı türünün sıralaması, daha az kesinlik ile işaretli tam sayı türünün sıralamasından daha büyük olacaktır.
    • Rütbesi rütbesinden long long intdaha büyük olacaktır, rütbesinden long intdaha büyük olacaktır, rütbesinden intdaha büyük olacaktır, rütbesinden short intdaha büyük olacaktır signed char.
    • İmzasız herhangi bir tamsayı türünün sırası, varsa, karşılık gelen işaretli tamsayı türünün sırasına eşit olacaktır.
    • Herhangi bir standart tamsayı türünün sıralaması, aynı genişliğe sahip herhangi bir genişletilmiş tamsayı türünün sıralamasından daha büyük olmalıdır.
    • Rütbesiyle charrütbesi eşit olacaktır signed charve unsigned char.
    • Herhangi bir genişletilmiş imzalı tamsayı türünün aynı hassasiyete sahip başka bir genişletilmiş imzalı tamsayı türüne göre sıralaması uygulama tanımlıdır, ancak yine de tamsayı dönüştürme sırasını belirlemek için diğer kurallara tabidir.
    • T1, T2 ve T3 tüm tamsayı türleri için, eğer T1 T2'den daha yüksek rütbeye ve T2 T3'ten daha yüksek rütbeye sahipse, T1 T3'ten daha büyük rütbeye sahiptir.
  • Genel Aritmetik Dönüşümler:

    • Her iki işlenen de aynı türe sahipse, başka dönüştürmeye gerek yoktur.
    • Her iki işlenen de aynı tamsayı türündeyse (imzalı veya imzasız), daha az tamsayı dönüştürme sırası türüne sahip işlenen, daha büyük bir sıraya sahip işlenen türüne dönüştürülür.
    • İmzasız tamsayı türüne sahip işlenen, diğer işlenen türünün sıralamasından daha büyük veya ona eşitse, işaretli tamsayı türüne sahip işlenen, imzalanmamış tamsayı türüne sahip işlenen türüne dönüştürülür.
    • İşaretli tamsayı türüne sahip işlenen türü, işaretsiz tamsayı türüne sahip işlenen türünün tüm değerlerini temsil edebiliyorsa, işaretsiz tamsayı türüne sahip işlenen, işaretli tamsayı türüne sahip işlenen türüne dönüştürülür.
    • Aksi takdirde, her iki işlenen de işaretli tamsayı tipine sahip işlenen tipine karşılık gelen imzasız tamsayı tipine dönüştürülür. Belirli işlemler, olağan aritmetik işlemlerin anlambilimine ekleyebilir veya bunları değiştirebilir.

1

Bölüm 4'ün tamamı dönüşümlerden bahsediyor, ancak bence çoğunlukla bunlarla ilgilenmelisiniz:

4.5 İntegral promosyonlar [conv.prom]
char, işaretli karakter, işaretsiz karakter, kısa int veya işaretsiz kısa int değeri, int kaynak türünün tüm değerlerini temsil edebiliyorsa int türünde bir değere dönüştürülebilir; aksi
takdirde, kaynak değer işareti imzalanmamış int türünden bir değer değerine dönüştürülebilir.
Wchar_t (3.9.1) türü bir numaralandırma veya numaralandırma türü (7.2)
, alttaki türünün tüm değerlerini temsil edebilecek aşağıdaki türlerden ilkinin bir değerine dönüştürülebilir: int, unsigned int,
long veya unsigned uzun.
İnt
bit alanının tüm değerlerini temsil edebiliyorsa, tümleşik bir bit alanı (9.6) için bir değer int türünde bir değere dönüştürülebilir; aksi takdirde, imzalanmamış int tekrarlanabilirse imzalanmamış int'e dönüştürülebilir.
bit alanının tüm değerlerini yeniden gönderir. Bit alanı henüz daha büyükse, tümleşik bir tanıtım uygulanmaz. Eğer
bit alan bir numaralandırılmış türü vardır, tanıtım amaçlı bu tür başka bir değer olarak kabul edilir.
Bool türü bir değer, int türünün bir değerine dönüştürülebilir, yanlış sıfır olur ve gerçek
bir olur.
Bu dönüşümlere ayrılmaz tanıtımlar denir.

4.6 Kayan nokta tanıtımı [conv.fpprom]
Float türü bir değer, double türünün bir değerine dönüştürülebilir. Değer değişmez.
Bu dönüşüme kayan nokta tanıtımı denir.

Bu nedenle, float içeren tüm dönüşümler - sonuç float olur.

Her ikisini de içeren yalnızca int - sonuç int: int / int = int


1

İfadenin türü, her iki parça aynı tipte olmadığında, her ikisinin de en büyüğüne dönüştürülecektir . Buradaki sorun hangisinin diğerinden daha büyük olduğunu anlamaktır (bayt cinsinden boyutla ilgisi yoktur).

Gerçek sayının ve bir tamsayı sayısının yer aldığı ifadelerde, tamsayı gerçek sayıya yükseltilir. Örneğin, int + float'ta, ifadenin türü float'tır.

Diğer fark, türün kabiliyeti ile ilgilidir. Örneğin, int ve long int içeren bir ifade long int türünden kaynaklanır.


2
Bu doğru değil. Platformlarda a long, a'dan "büyük" olabilir , floatancak long+ türü floatnedir?
CB Bailey

1
-1: Eğer neyi kastediyorsunuz büyük ? Bir şamandıra int'ten daha mı büyük ? Yoksa tam tersi ?
Paul R

2
Yorumlarınız için teşekkür ederim. Evet, burada bayt cinsinden boyut hiç ilgi görmüyor. Ortaya çıktığı gibi, italik olarak en büyük ifadeyi koymak, cevabı açıklamak için yeterli değildir. Her neyse, daha derin bir şekilde açıklamak mantıklı değil, çünkü şimdi başka, çok kapsamlı cevaplar var.
Baltasarq

-2

Uyarı!

Dönüşümler soldan sağa doğru gerçekleşir.

Bunu dene:

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

9
Bu dönüşümden değil, operatör önceliğinden kaynaklanmaktadır. j + i * k101. sonuçlanır.
gartenriese
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.