Olhando para este código C #:
byte x = 1;
byte y = 2;
byte z = x + y; // ERROR: Cannot implicitly convert type 'int' to 'byte'
O resultado de qualquer matemática realizada em byte
(ou short
) tipos é implicitamente convertido de volta para um número inteiro. A solução é converter explicitamente o resultado em um byte:
byte z = (byte)(x + y); // this works
O que eu estou querendo saber é por quê? É arquitetônico? Filosófico?
Nós temos:
int
+int
=int
long
+long
=long
float
+float
=float
double
+double
=double
Então por que não:
byte
+byte
=byte
short
+short
=short
?
Um pouco de fundo: estou executando uma longa lista de cálculos sobre "pequenos números" (ou seja, <8) e armazenando os resultados intermediários em uma grande variedade. O uso de uma matriz de bytes (em vez de uma matriz int) é mais rápido (devido a ocorrências de cache). Mas a extensa conversão de bytes espalhada pelo código o torna muito mais ilegível.
c#
type-conversion
Robert Cartaino
fonte
fonte
byte1 | byte2
não os trata como números. Isso é tratá-los precisamente como padrões de bits. Entendo o seu ponto de vista, mas acontece que toda vez que eu fazia aritmética em bytes em C #, eu os tratava como bits, não como números, e esse comportamento está sempre no caminho.Respostas:
A terceira linha do seu trecho de código:
na verdade significa
Portanto, não há + operação em bytes, os bytes são convertidos primeiro em números inteiros e o resultado da adição de dois números inteiros é um número inteiro (32 bits).
fonte
int
está ocorrendo: stackoverflow.com/a/43578929/4561887Em termos de "por que isso acontece", é porque não existem operadores definidos pelo C # para aritmética com byte, sbyte, curto ou ushort, como outros já disseram. Esta resposta é sobre por que esses operadores não estão definidos.
Eu acredito que é basicamente por uma questão de desempenho. Os processadores têm operações nativas para fazer aritmética com 32 bits muito rapidamente. A conversão da conversão do resultado para um byte automaticamente poderia ser feita automaticamente , mas resultaria em penalidades de desempenho no caso em que você realmente não deseja esse comportamento.
Eu acho que isso é mencionado em um dos padrões C # anotados. Olhando...
EDIT: Irritantemente, agora eu examinei as especificações anotadas do ECMA C # 2, as especificadas do MS C # 3 e a especificação da CLI da anotação, e nenhuma delas menciona isso até onde posso ver. Tenho certeza de que vi o motivo exposto acima, mas estou impressionado se souber onde. Desculpas, fãs de referência :(
fonte
Eu pensei que já tinha visto isso em algum lugar antes. A partir deste artigo, The Old New Thing :
EDIT : Raymond está defendendo, essencialmente, a abordagem C e C ++ adotada originalmente. Nos comentários, ele defende o fato de que o C # adota a mesma abordagem, com base na compatibilidade com versões anteriores da linguagem.
fonte
C #
O ECMA-334 declara que a adição é definida apenas como legal em int + int, uint + uint, longo + longo e ulong + ulong (ECMA-334 14.7.4). Como tal, estas são as operações candidatas a serem consideradas em relação ao 14.4.2. Como existem projeções implícitas de byte a int, uint, long e ulong, todos os membros da função de adição são membros da função aplicáveis em 14.4.2.1. Temos que encontrar o melhor elenco implícito pelas regras em 14.4.2.3:
A conversão (C1) para int (T1) é melhor que a conversão (C2) para uint (T2) ou ulong (T2) porque:
A conversão (C1) para int (T1) é melhor que a conversão (C2) para longa (T2) porque há uma conversão implícita de int para longa:
Portanto, a função int + int é usada, que retorna um int.
O que é uma maneira muito longa de dizer que está enterrado profundamente na especificação C #.
CLI
A CLI opera apenas em 6 tipos (int32, int nativo, int64, F, O e &). (Seção 1.5 da partição 3 do ECMA-335)
O byte (int8) não é um desses tipos e é automaticamente coagido a um int32 antes da adição. (Partição ECMA-335, seção 1.6)
fonte
byte3 = byte1 And byte2
sem uma conversão, mas inútil lançará uma exceção de tempo de execução seint1 = byte1 + byte2
gerar um valor acima de 255. Não sei se algum idioma permitiriabyte3 = byte1+byte2
e lançaria uma exceção quando isso exceder 255, mas não lançará uma exceção seint1 = byte1+byte2
gerar um valor no intervalo 256-510.As respostas que indicam alguma ineficiência na adição de bytes e no truncamento do resultado em um byte estão incorretas. Os processadores x86 possuem instruções especificamente projetadas para operação com números inteiros em quantidades de 8 bits.
De fato, para processadores x86 / 64, executar operações de 32 ou 16 bits é menos eficiente que as operações de 64 ou 8 bits devido ao byte do prefixo do operando que precisa ser decodificado. Em máquinas de 32 bits, a execução de operações de 16 bits implica a mesma penalidade, mas ainda existem códigos de operação dedicados para operações de 8 bits.
Muitas arquiteturas RISC têm instruções nativas similares semelhantes à palavra / byte. Aqueles que geralmente não têm um valor de armazenar e converter em valor assinado de algum tamanho de bit.
Em outras palavras, essa decisão deve ter sido baseada na percepção do tipo de byte, não devido às ineficiências subjacentes do hardware.
fonte
byte
(echar
), mas não para oshort
qual semanticamente é claramente um número.Lembro-me de uma vez que li algo de Jon Skeet (não consigo encontrá-lo agora, continuarei procurando) sobre como o byte não sobrecarrega o operador +. De fato, ao adicionar dois bytes como em sua amostra, cada byte está na verdade sendo convertido implicitamente em um int. O resultado disso é obviamente um int. Agora, por que isso foi projetado dessa maneira, vou esperar o próprio Jon Skeet postar :)
EDIT: Encontrei! Ótimas informações sobre esse mesmo tópico aqui .
fonte
Isto é devido ao estouro e transporta.
Se você adicionar dois números de 8 bits, eles poderão transbordar para o nono bit.
Exemplo:
Eu não sei ao certo, mas eu suponho que
ints
,longs
edoubles
são dadas mais espaço, porque eles são muito grande como ele é. Além disso, são múltiplos de 4, que são mais eficientes para os computadores manipularem, devido à largura do barramento de dados interno ter 4 bytes ou 32 bits (64 bits está se tornando mais prevalente agora). Byte e curto são um pouco mais ineficientes, mas podem economizar espaço.fonte
Na especificação da linguagem C # 1.6.7.5 7.2.6.2 Promoções numéricas binárias, ele converte os dois operandos em int se não puder ajustá-lo em várias outras categorias. Meu palpite é que eles não sobrecarregaram o operador + para usar o byte como parâmetro, mas querem que ele atue de maneira um pouco normal, para que apenas usem o tipo de dados int.
Linguagem C # Spec
fonte
Minha suspeita é que o C # esteja realmente chamando o
operator+
definido emint
(que retorna um aint
menos que você esteja em umchecked
bloco) e implicitamente convertendo ambos os seusbytes
/shorts
paraints
. É por isso que o comportamento parece inconsistente.fonte
Provavelmente, essa foi uma decisão prática por parte dos designers de linguagem. Afinal, um int é um Int32, um número inteiro assinado de 32 bits. Sempre que você fizer uma operação inteira em um tipo menor que int, ela será convertida em um int assinado de 32 bits pela maioria das CPUs de 32 bits. Isso, combinado com a probabilidade de transbordar números inteiros pequenos, provavelmente selou o acordo. Isso evita a tarefa de verificar continuamente se há excesso / subfluxo, e quando o resultado final de uma expressão em bytes estaria dentro do alcance, apesar do fato de que, em algum estágio intermediário, estaria fora do alcance, você obtém uma resposta correta. resultado.
Outro pensamento: o excesso / subfluxo desses tipos teria que ser simulado, pois não ocorreria naturalmente nas CPUs-alvo mais prováveis. Porque se importar?
fonte
Esta é em grande parte a minha resposta que refere a este tema, apresentado pela primeira vez a uma pergunta semelhante aqui .
Todas as operações com números inteiros menores que Int32 são arredondadas para 32 bits antes do cálculo, por padrão. A razão pela qual o resultado é Int32 é simplesmente deixá-lo como está após o cálculo. Se você verificar os opcodes aritméticos do MSIL, o único tipo numérico integral com o qual eles operam é Int32 e Int64. É "por design".
Se você deseja o resultado de volta no formato Int16, é irrelevante se você executar a conversão no código ou se o compilador (hipoteticamente) emitir a conversão "under the hood".
Por exemplo, para fazer a aritmética Int16:
Os dois números seriam expandidos para 32 bits, adicionados e truncados novamente para 16 bits, que é como a MS pretendia que fosse.
A vantagem de usar curto (ou byte) é principalmente armazenamento nos casos em que você possui grandes quantidades de dados (dados gráficos, streaming, etc.)
fonte
A adição não está definida para bytes. Então eles são convertidos para int para a adição. Isso vale para a maioria das operações matemáticas e bytes. (observe que é assim que costumava ser em idiomas mais antigos, suponho que seja válido hoje).
fonte
Eu acho que é uma decisão de projeto sobre qual operação era mais comum ... Se byte + byte = byte talvez muito mais pessoas se incomodem por ter que converter para int quando um int for necessário como resultado.
fonte
Do código do .NET Framework:
Simplifique com o .NET 3.5 e superior:
agora você pode fazer:
fonte
Testei o desempenho entre byte e int.
Com valores int:
Com valores de bytes:
Aqui está o resultado:
byte: 3.57s 157mo, 3.71s 171mo, 3.74s 168mo com CPU ~ = 30%
int: 4.05s 298mo, 3.92s 278mo, 4.28 294mo com CPU ~ = 27%
Conclusão: o
byte usa mais a CPU, mas ela custa memória e é mais rápido (talvez porque haja menos bytes a serem alocados)
fonte
Além de todos os outros ótimos comentários, pensei em acrescentar um pequeno detalhe. Muitos comentários se perguntam por que int, long e praticamente qualquer outro tipo numérico também não segue essa regra ... retorna um tipo "maior" em resposta à aritmética.
Muitas respostas tiveram a ver com desempenho (bem, 32 bits é mais rápido que 8 bits). Na realidade, um número de 8 bits ainda é um número de 32 bits para uma CPU de 32 bits .... mesmo se você adicionar dois bytes, o pedaço de dados em que a CPU opera será 32 bits independentemente ... portanto, adicionar ints não será necessário. ser "mais rápido" do que adicionar dois bytes ... é tudo igual à CPU. AGORA, a adição de duas entradas será mais rápida do que a adição de dois comprimentos em um processador de 32 bits, porque a adição de dois comprimentos requer mais microops, pois você trabalha com números maiores que a palavra do processador.
Eu acho que a razão fundamental para fazer com que a aritmética de bytes resulte em ints é bastante clara e direta: 8 bits não chega muito longe! : D Com 8 bits, você tem um intervalo não assinado de 0 a 255. Isso não é um monte de espaço para trabalhar com ... a probabilidade de que você vai correr em uma bytes limitações é muito alto quando usá-los em aritmética. No entanto, a chance de você ficar sem bits ao trabalhar com ints, longs ou dobros, etc. é significativamente menor ... baixa o suficiente para que raramente encontramos a necessidade de mais.
A conversão automática de byte para int é lógica porque a escala de um byte é muito pequena. A conversão automática de int para long, float para double, etc. não é lógica porque esses números têm uma escala significativa.
fonte
byte - byte
os retornosint
, ou porque eles não lançam parashort
...byte + byte
retornaint
, porque 255 + qualquer coisa é maior do que um byte pode conter, não faz sentido que nenhum byte menos qualquer outro byte retorne algo além de um int do ponto de vista da consistência do tipo de retorno.byte
subtração retornaria abyte
e a adição de bytes retornaria ashort
(byte
+byte
sempre caberá em ashort
). Se fosse sobre consistência, como você diz,short
ainda seria suficiente para ambas as operações e nãoint
. Claramente, há uma mistura de razões, nem todas necessariamente bem pensadas. Ou o motivo do desempenho fornecido abaixo pode ser mais preciso.