Regras implícitas de conversão de tipos em operadores C ++

167

Eu quero ser melhor em saber quando devo lançar. Quais são as regras implícitas de conversão de tipos em C ++ ao adicionar, multiplicar etc. Por exemplo,

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

et cetera ...

A expressão será sempre avaliada como o tipo mais preciso? As regras diferem para Java? Corrija-me se eu tiver formulado esta pergunta incorretamente.

Matt Montag
fonte
16
Lembre-se de que ^é XOR.
GManNickG
16
@int ^ flutuador = erro de compilação :)
Serge Dundich

Respostas:

223

Em operadores C ++ (para tipos de POD) sempre agem em objetos do mesmo tipo.
Assim, se não forem os mesmos, um será promovido para corresponder ao outro.
O tipo do resultado da operação é o mesmo dos operandos (após a conversão).

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

Nota. O tamanho mínimo das operações é int. Então short/ charsão promovidos a intantes da operação é feito.

Em todas as suas expressões, o valor inté promovido a floatantes de a operação ser executada. O resultado da operação é 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
fonte
1
"O tamanho mínimo das operações é int." - Isso seria muito estranho (e as arquiteturas que oferecem suporte eficiente a operações char / short?) Isso está realmente na especificação C ++?
Rafał Dowgird 6/04/11
3
@ Rafal: Sim. int deve ser o tipo inteiro mais eficiente para operação em uma plataforma específica. char deve sempre ser 1, mas curto pode ter o mesmo tamanho que int.
Martin York
1
@ Rafał: sim, é muito estranho e está no padrão. Em muitos casos, a arquitetura que você descreve pode usar seu chartipo supereficiente . Se o valor de char + charfor atribuído a a char, ele poderá fazer a aritmética chare, por exemplo, contornar. Mas se o resultado for atribuído int, ele deverá fazer a aritmética em um tipo grande o suficiente para obter o resultado correto quando for maior que CHAR_MAX.
21711 Steve Jobs (
2
Eu só quero enfatizar o fato de que int é promovido a unsigned int !!! Eu luto com bugs há dias porque tinha a impressão de que ambos seriam promovidos para int ou long para que um possível resultado negativo não causasse um underflow / wrap-around.
Nitsas
10
Exemplo do problema " int é promovido a int sem sinal ": ((int) 4) - ((unsigned int) 5)resultará em 4294967295entradas de 32 bits e entradas de sinal de 32 bits.
Nitsas
33

Operações aritméticas envolvendo floatresultados em float.

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

Para uma resposta mais detalhada. Veja o que diz a seção §5 / 9 do padrão C ++

Muitos operadores binários que esperam operandos do tipo aritmético ou de enumeração causam conversões e geram tipos de resultados de maneira semelhante. O objetivo é produzir um tipo comum, que também é o tipo do resultado .

Esse padrão é chamado de conversões aritméticas usuais, definidas da seguinte forma:

- Se um operando for do tipo long double, o outro será convertido em long double.

- Caso contrário, se um dos operandos for duplo, o outro será convertido para o dobro.

- Caso contrário, se um operando for flutuante, o outro será convertido em flutuante.

- Caso contrário, as promoções integrais (4.5) serão realizadas nos dois operandos.54)

- Então, se um dos operandos não tiver assinado por muito tempo, o outro será convertido em não assinado por muito tempo.

- Caso contrário, se um operando for um int longo e outro int não assinado, se um int longo puder representar todos os valores de um int não assinado, o int não assinado será convertido em um int longo; caso contrário, ambos os operandos serão convertidos em int longo não assinado.

- Caso contrário, se um operando for longo, o outro será convertido em longo.

- Caso contrário, se um dos operandos não estiver assinado, o outro será convertido em não assinado.

[Nota: caso contrário, o único caso restante é que ambos os operandos sejam int]

Nawaz
fonte
3
... desde que o outro tipo não seja nem doublenem long double.
perfil completo de CB Bailey
1
@ Charles: Correto. Eu citei a seção relevante da Norma para esclarecer melhor.
Nawaz
Então, um número inteiro sempre pode ser convertido para flutuar sem perda de dados? (por exemplo, zerando o expoente e usando tudo para a mantissa)?
Marco A.
1
Esta resposta está desatualizada. Sugerir atualização. Em particular, long longe unsigned longnão abordado aqui.
Chux - Reintegrar Monica
@MarcoA. um de 32 bits floatnão possui bits suficientes na mantissa (24 bits para IEEE-754 ) para um de 32 bits int; portanto, pode haver perda de dados. Um de 64 bits doubledeve estar bem.
Mark Ransom
17

Como as outras respostas não falam sobre as regras do C ++ 11, aqui está uma. Do padrão C ++ 11 (rascunho n3337) §5 / 9 (enfatizava a diferença):

Esse padrão é chamado de conversões aritméticas usuais , definidas da seguinte forma:

- Se um dos operandos for do tipo de enumeração no escopo, nenhuma conversão será executada; se o outro operando não tiver o mesmo tipo, a expressão será mal formada.

- Se um operando for do tipo long double, o outro será convertido em long double.

- Caso contrário, se um dos operandos for duplo, o outro será convertido para o dobro.

- Caso contrário, se um operando for flutuante, o outro será convertido em flutuante.

- Caso contrário, as promoções integrais serão realizadas nos dois operandos. Em seguida, as seguintes regras serão aplicadas aos operandos promovidos:

- Se os dois operandos tiverem o mesmo tipo, nenhuma conversão adicional será necessária.

- Caso contrário, se os dois operandos tiverem tipos inteiros assinados ou os tipos inteiros não assinados, o operando com o tipo de classificação menor de conversão de número inteiro será convertido no tipo de operando com maior classificação.

- Caso contrário, se o operando que possui o tipo inteiro não assinado tiver uma classificação maior ou igual à classificação do tipo do outro operando, o operando com o tipo inteiro assinado deverá ser convertido no tipo de operando com o tipo inteiro não assinado.

- Caso contrário, se o tipo do operando com tipo inteiro assinado puder representar todos os valores do tipo do operando com tipo inteiro não assinado, o operando com tipo inteiro não assinado deverá ser convertido no tipo do operando com tipo inteiro assinado.

- Caso contrário, os dois operandos deverão ser convertidos no tipo inteiro não assinado correspondente ao tipo do operando com o tipo inteiro assinado.

Veja aqui uma lista atualizada com frequência.

legends2k
fonte
1
Estas regras são as mesmas em todas as versões do C ++, excepto para as contagens escopo que foram adicionados em C ++ 11 naturalmente
MM
6

Esta resposta é direcionada em grande parte a um comentário feito por @ RafałDowgird:

"O tamanho mínimo das operações é int." - Isso seria muito estranho (e as arquiteturas que oferecem suporte eficiente a operações char / short?) Isso está realmente na especificação C ++?

Lembre-se de que o padrão C ++ possui a regra "como se". Consulte a seção 1.8: Execução do programa:

3) Esta disposição às vezes é chamada de regra "como se", porque uma implementação é livre para desconsiderar qualquer requisito da Norma, desde que o resultado seja como se o requisito tivesse sido obedecido, na medida em que possa ser determinado pelo observável comportamento do programa.

O compilador não pode definir um inttamanho de 8 bits, mesmo que seja o mais rápido, pois o padrão exige um mínimo de 16 bits int.

Portanto, no caso de um computador teórico com operações super rápidas de 8 bits, a promoção implícita intpara aritmética pode ter importância. No entanto, para muitas operações, você não pode dizer se o compilador realmente fez as operações na precisão de um inte depois foi convertido em um charpara armazenar em sua variável, ou se as operações foram realizadas no char o tempo todo.

Por exemplo, considere unsigned char = unsigned char + unsigned char + unsigned char, onde a adição estouraria (vamos assumir um valor de 200 para cada). Se você promoveu para int, obteria 600, que seriam implicitamente convertidos em um unsigned char, que envolveria o módulo 256, resultando em um resultado final de 88. Se você não realizasse essas promoções, teria que agrupar entre as primeiras duas adições, o que reduziria o problema de 200 + 200 + 200para 144 + 200, que é 344, e reduz para 88. Em outras palavras, o programa não sabe a diferença, portanto o compilador pode ignorar o mandato de executar operações intermediárias intse os operandos tiverem uma classificação inferior a int.

Isso é verdade em geral de adição, subtração e multiplicação. Não é verdade em geral para divisão ou módulo.

David Stone
fonte
4

Se você excluir os tipos não assinados, haverá uma hierarquia ordenada: char assinado, curto, int, longo, longo, flutuante, duplo, longo duplo. Primeiro, qualquer coisa que venha antes de int acima será convertida em int. Em uma operação binária, o tipo de classificação mais baixa será convertido para o mais alto e os resultados serão do tipo de maior. (Você notará que, a partir da hierarquia, sempre que um ponto flutuante e um tipo integral estiverem envolvidos, o tipo integral será convertido no tipo de ponto flutuante.)

Não assinado complica um pouco as coisas: perturba a classificação e partes da classificação tornam-se definidas para implementação. Por isso, é melhor não combinar assinado e não assinado na mesma expressão. (A maioria dos especialistas em C ++ parece evitar a assinatura, a menos que operações bit a bit estejam envolvidas. Isso é, pelo menos, o que Stroustrup recomenda.)

James Kanze
fonte
3
Stroustrup pode recomendar o que ele gosta, mas usar um sinal intpara um número que nunca precisa ser negativo é um desperdício completo de 50% da faixa disponível. Certamente não sou Stroustrup, mas uso unsignedpor padrão e signedsomente quando tenho um motivo.
Underscore_d
1
Tudo bem, sublinhado_d, até o dia em que você deve subtrair. O principal problema com números não assinados em C ++ é que, quando você executa a subtração, eles permanecem sem assinatura. Então, suponha que você escreva uma função para ver se um vetor std :: está em ordem. Você pode escrever bool in_order(vector<T> vec) { for ( int i = 0; i < size() - 1; ++i) { if (vec[i + 1] < vec[i]) return false; } return true;e então você estaria irritado ao descobrir que ele trava para vetores vazios porque size () - 1 retorna 18446744073709551615.
jorgbrown
3

Minha solução para o problema tem WA (resposta errada), então eu mudei um de intpara long long inte deu AC (aceitar) . Anteriormente, eu estava tentando fazer long long int += int * inte depois de corrigi-lo long long int += long long int * int. Pesquisando no Google,

1. Conversões aritméticas

Condições para conversão de tipo:

Condições atendidas ---> Conversão

  • Qualquer operando é do tipo long double . ---> Outro operando é convertido para o tipo long double .

  • A condição precedente não foi atendida e qualquer operando é do tipo duplo . ---> Outro operando é convertido para o tipo double .

  • Condições precedentes não atendidas e qualquer operando é do tipo float . ---> Outro operando é convertido para o tipo float .

  • Condições precedentes não atendidas (nenhum dos operandos é do tipo flutuante). ---> Promoções integrais são realizadas nos operandos da seguinte maneira:

    • Se um operando for do tipo não assinado por muito tempo , o outro operando será convertido para o tipo não assinado por muito tempo .
    • Se a condição anterior não for atendida e se um operando for do tipo long e o outro do tipo não assinado int , os dois operandos serão convertidos para o tipo não assinado por muito tempo .
    • Se as duas condições anteriores não forem atendidas e se um dos operandos for do tipo long , o outro operando será convertido no tipo long .
    • Se as três condições anteriores não forem atendidas e se um dos operandos for do tipo unsigned int , o outro operando será convertido no tipo unsigned int .
    • Se nenhuma das condições anteriores for atendida, os dois operandos serão convertidos no tipo int .

2) Regras de conversão de número inteiro

  • Promoções Inteiras:

Tipos inteiros menores que int são promovidos quando uma operação é executada neles. Se todos os valores do tipo original puderem ser representados como um int, o valor do tipo menor será convertido em um int; caso contrário, é convertido em um int sem sinal. Promoções inteiras são aplicadas como parte das conversões aritméticas usuais em certas expressões de argumento; operandos dos operadores +, - e ~ unários; e operandos dos operadores de turno.

  • Classificação de conversão de número inteiro:

    • Dois tipos inteiros assinados não devem ter a mesma classificação, mesmo que tenham a mesma representação.
    • A classificação de um tipo inteiro assinado deve ser maior que a classificação de qualquer tipo inteiro assinado com menos precisão.
    • A classificação de long long intdeve ser maior que a classificação de long int, que deve ser maior que a classificação de int, que deve ser maior que a classificação de short int, que deve ser maior que a classificação de signed char.
    • A classificação de qualquer tipo inteiro não assinado deve ser igual à classificação do tipo inteiro assinado assinado, se houver.
    • A classificação de qualquer tipo inteiro padrão deve ser maior que a classificação de qualquer tipo inteiro estendido com a mesma largura.
    • A classificação de charserá igual à classificação de signed chare unsigned char.
    • A classificação de qualquer tipo de número inteiro assinado estendido em relação a outro tipo de número inteiro assinado estendido com a mesma precisão é definida pela implementação, mas ainda sujeita às outras regras para determinar a classificação de conversão de número inteiro.
    • Para todos os tipos inteiros T1, T2 e T3, se T1 tiver uma classificação maior que T2 e T2 tiver uma classificação maior que T3, T1 terá uma classificação maior que T3.
  • Conversões aritméticas comuns:

    • Se os dois operandos tiverem o mesmo tipo, nenhuma conversão adicional será necessária.
    • Se os dois operandos forem do mesmo tipo inteiro (assinado ou não), o operando com o tipo de classificação de conversão de número menor será convertido no tipo de operando com maior classificação.
    • Se o operando que possui o tipo inteiro não assinado tiver uma classificação maior ou igual à classificação do tipo do outro operando, o operando com o tipo inteiro assinado será convertido no tipo do operando com o tipo inteiro não assinado.
    • Se o tipo do operando com o tipo inteiro assinado puder representar todos os valores do tipo do operando com o tipo inteiro não assinado, o operando com o tipo inteiro não assinado será convertido no tipo do operando com o tipo inteiro assinado.
    • Caso contrário, os dois operandos serão convertidos no tipo inteiro não assinado correspondente ao tipo do operando com o tipo inteiro assinado. Operações específicas podem adicionar ou modificar a semântica das operações aritméticas usuais.
garakchy
fonte
1

Todo o capítulo 4 fala sobre conversões, mas acho que você deve se interessar principalmente por estas:

4.5 Promoções integrais [conv.prom]
Um rvalor do tipo char, assinado, char não assinado, short int ou short curto não assinado pode ser convertido em um rvalor do tipo int se int puder representar todos os valores do tipo de origem; caso
contrário, o rvalue de origem pode ser convertido em um rvalue do tipo unsigned int.
Um rvalue do tipo wchar_t (3.9.1) ou um tipo de enumeração (7.2) pode ser convertido em um rvalue do primeiro
dos seguintes tipos que podem representar todos os valores do seu tipo subjacente: int, sem sinal int,
longo ou sem sinal longo.
Um rvalue para um campo de bits integral (9.6) pode ser convertido em um rvalue do tipo int se int puder representar todos
os valores do campo de bits; caso contrário, ele pode ser convertido em unsigned int se int não assinado puder
reenviar todos os valores do campo de bits. Se o campo de bits ainda for maior, nenhuma promoção integral se aplicará a ele. Se o
campo de bits tiver um tipo enumerado, será tratado como qualquer outro valor desse tipo para fins de promoção.
Um rvalue do tipo bool pode ser convertido em um rvalue do tipo int, com false se tornando zero e true
se tornando um.
Essas conversões são chamadas de promoções integrais.

4.6 Promoção de ponto flutuante [conv.fpprom]
Um rvalue do tipo float pode ser convertido em um rvalue do tipo double. O valor é inalterado.
Essa conversão é chamada de promoção de ponto flutuante.

Portanto, todas as conversões envolvendo float - o resultado é float.

Somente o que envolve os dois int - o resultado é int: int / int = int

BЈовић
fonte
1

O tipo da expressão, quando as duas partes não forem do mesmo tipo, será convertido na maior das duas. O problema aqui é entender qual é maior que o outro (não tem nada a ver com tamanho em bytes).

Nas expressões em que um número real e um número inteiro estão envolvidos, o número inteiro será promovido para um número real. Por exemplo, em int + float, o tipo da expressão é float.

A outra diferença está relacionada à capacidade do tipo. Por exemplo, uma expressão envolvendo int e long int resultará do tipo long int.

Baltasarq
fonte
2
Isso não é verdade. Em algumas plataformas a longé "maior" que a floatmas qual é o tipo de long+ float?
perfil completo de CB Bailey
1
-1: O que você quer dizer com maior ? Um float é maior que um int? Ou vice-versa ?
Paul R
2
Obrigado por seus comentários. Sim, o tamanho em bytes aqui não interessa nada. Como se vê, obviamente colocar o maior em itálico não é suficiente para explicar a resposta. De qualquer forma, não faz sentido explicá-lo mais profundamente, pois agora existem outras respostas muito completas.
Baltasarq
-2

Embargo!

As conversões ocorrem da esquerda para a direita.

Tente o seguinte:

int i = 3, j = 2;
double k = 33;
cout << k * j / i << endl; // prints 22
cout << j / i * k << endl; // prints 0
Habib
fonte
9
Isso não se deve à conversão, mas à precedência do operador. j + i * kresultaria em 101.
gartenriese