variable tipos long example datos c# type-conversion

long - tipos de datos en c#



byte+byte=int... ¿por qué? (16)

Mirando este código C #:

byte x = 1; byte y = 2; byte z = x + y; // ERROR: Cannot implicitly convert type ''int'' to ''byte''

El resultado de cualquier cálculo matemático realizado en tipos de byte (o short ) se devuelve implícitamente a un entero. La solución es devolver explícitamente el resultado a un byte:

byte z = (byte)(x + y); // this works

Lo que me pregunto es por qué? ¿Es arquitectónico? ¿Filosófico?

Tenemos:

  • int + int = int
  • long + long = long
  • float + float = float
  • double + double = double

Entonces por qué no:

  • byte + byte = byte
  • short + short = short ?

Un poco de fondo: estoy realizando una larga lista de cálculos en "números pequeños" (es decir, <8) y almacenando los resultados intermedios en una gran matriz. Usar una matriz de bytes (en lugar de una matriz int) es más rápido (debido a los aciertos de caché). Pero el extenso byte-cast distribuido a través del código hace que sea mucho más ilegible.


DO#

ECMA-334 establece que la adición solo se define como legal en int + int, uint + uint, long + long y ulong + ulong (ECMA-334 14.7.4). Como tales, estas son las operaciones candidatas que deben considerarse con respecto a 14.4.2. Debido a que hay conversiones implícitas de byte a int, uint, long y ulong, todos los miembros de función de adición son miembros de función aplicables según 14.4.2.1. Tenemos que encontrar el mejor lanzamiento implícito por las reglas en 14.4.2.3:

La conversión (C1) a int (T1) es mejor que la conversión (C2) a uint (T2) o ulong (T2) porque:

  • Si T1 es int y T2 es uint, o ulong, C1 es la mejor conversión.

La conversión (C1) a int (T1) es mejor que la conversión (C2) a long (T2) porque hay una conversión implícita de int a long:

  • Si existe una conversión implícita de T1 a T2, y no existe una conversión implícita de T2 a T1, C1 es la mejor conversión.

Por lo tanto, se utiliza la función int + int, que devuelve un int.

Lo que es un largo camino para decir que está enterrado en la especificación C #.

CLI

El CLI opera solo en 6 tipos (int32, int nativo, int64, F, O y &). (ECMA-335 partición 3 sección 1.5)

Byte (int8) no es uno de esos tipos, y se obliga automáticamente a un int32 antes de la adición. (ECMA-335 partición 3 sección 1.6)


Además de todos los otros grandes comentarios, pensé que agregaría un pequeño chisme. Muchos comentarios se han preguntado por qué int, long y prácticamente cualquier otro tipo numérico no sigue esta regla ... devuelve un tipo "más grande" en respuesta a arithmatic.

Muchas respuestas han tenido que ver con el rendimiento (bueno, 32 bits es más rápido que 8 bits). En realidad, un número de 8 bits sigue siendo un número de 32 bits para una CPU de 32 bits ... incluso si agrega dos bytes, la porción de datos en la que opera la CPU será de 32 bits independientemente ... así que agregar entradas no se va a realizar. ser "más rápido" que agregar dos bytes ... todo es lo mismo para la CPU. AHORA, agregar dos ints SERÁ más rápido que agregar dos largos en un procesador de 32 bits, porque agregar dos largos requiere más microops ya que estás trabajando con números más anchos que la palabra de los procesadores.

Creo que la razón fundamental para que la aritmética de bytes resulte en ints es bastante clara y directa: ¡8bits simplemente no llega muy lejos! : D Con 8 bits, tiene un rango sin firmar de 0-255. No es mucho espacio con el que trabajar ... la probabilidad de que se encuentre con una limitación de bytes es MUY alta cuando se usan en aritmética. Sin embargo, la posibilidad de que se quede sin bits cuando trabaje con ints, largos, dobles, etc. es significativamente menor ... lo suficientemente bajo como para que raramente encontremos la necesidad de más.

La conversión automática de byte a int es lógica porque la escala de un byte es muy pequeña. La conversión automática de int a long, float to double, etc. no es lógica porque esos números tienen una escala significativa.


Creo que es una decisión de diseño sobre qué operación fue más común ... Si byte + byte = byte, tal vez a muchas más personas les molestará tener que pasar a int cuando se necesita un int como resultado.


De la especificación de lenguaje C # 1.6.7.5 7.2.6.2 Promociones numéricas binarias, convierte ambos operandos en int si no puede ajustarse en otras categorías. Supongo que no sobrecargaron al operador + para tomar el byte como parámetro, pero quieren que actúe de forma normal, por lo que solo usan el tipo de datos int.

Lenguaje C # espec.


Desde el código .NET Framework:

// bytes private static object AddByte(byte Left, byte Right) { short num = (short) (Left + Right); if (num > 0xff) { return num; } return (byte) num; } // shorts (int16) private static object AddInt16(short Left, short Right) { int num = Left + Right; if ((num <= 0x7fff) && (num >= -32768)) { return (short) num; } return num; }

Simplifique con .NET 3.5 y superior:

public static class Extensions { public static byte Add(this byte a, byte b) { return (byte)(a + b); } }

ahora puedes hacer

byte a = 1, b = 2, c; c = a.Add(b);


En términos de "por qué sucede en absoluto" es porque no hay ningún operador definido por C # para la aritmética con byte, sbyte, short o ushort, como han dicho otros. Esta respuesta es sobre por qué esos operadores no están definidos.

Creo que es básicamente por el rendimiento. Los procesadores tienen operaciones nativas para hacer aritmética con 32 bits muy rápidamente. Se podría hacer la conversión del resultado a un byte de forma automática, pero se obtendrían penalizaciones de rendimiento en el caso de que realmente no se desee ese comportamiento.

Creo que esto se menciona en uno de los estándares C # anotados. Mirando...

EDIT: Molesto, ahora he revisado la especificación ECMA C # 2 anotada, la especificación MS C # 3 anotada y la especificación CLI de anotación, y ninguno de ellos menciona esto por lo que puedo ver. Estoy seguro de que he visto la razón dada anteriormente, pero estoy impresionado si sé dónde. Disculpas, fanáticos de referencia :(


Esta es, en su mayor parte, mi respuesta relacionada con este tema, que se envió primero a una pregunta similar here .

Todas las operaciones con números integrales más pequeños que Int32 se redondean hasta 32 bits antes del cálculo por defecto. La razón por la que el resultado es Int32 es simplemente dejarlo como está después del cálculo. Si verifica los códigos de operación aritméticos de MSIL, el único tipo numérico integral con el que operan son Int32 e Int64. Es "por diseño".

Si desea obtener el resultado nuevamente en formato Int16, es irrelevante si realiza la conversión en el código, o si el compilador (hipotéticamente) emite la conversión "debajo del capó".

Por ejemplo, para hacer aritmética Int16:

short a = 2, b = 3; short c = (short) (a + b);

Los dos números se expandirían a 32 bits, se agregarían y luego se truncarían de nuevo a 16 bits, que es lo que MS quería que fuera.

La ventaja de usar short (o byte) es principalmente el almacenamiento en los casos en que tiene grandes cantidades de datos (datos gráficos, transmisión, etc.)


Esta fue probablemente una decisión práctica por parte de los diseñadores de idiomas. Después de todo, un int es un Int32, un entero con signo de 32 bits. Siempre que realice una operación de enteros en un tipo más pequeño que int, se convertirá a un int. De 32 bits con la mayoría de las CPU de 32 bits. Eso, combinado con la probabilidad de desbordar enteros pequeños, probablemente selló el trato. Le ahorra la tarea de verificar continuamente el flujo excesivo / insuficiente, y cuando el resultado final de una expresión en bytes estaría dentro del rango, a pesar del hecho de que en alguna etapa intermedia estaría fuera del rango, obtendrá una respuesta correcta. resultado.

Otro pensamiento: el exceso / subflujo en estos tipos tendría que ser simulado, ya que no ocurriría naturalmente en las CPU objetivo más probables. ¿Por qué molestarse?


Esto se debe a desbordamiento y acarreo.

Si agrega dos números de 8 bits, pueden desbordarse al noveno bit.

Ejemplo:

1111 1111 + 0000 0001 ----------- 1 0000 0000

No lo sé con certeza, pero supongo que a los ints , los longs y los doubles les da más espacio porque son bastante grandes. Además, son múltiplos de 4, que son más eficientes para el manejo de las computadoras, debido a que el ancho del bus de datos interno es de 4 bytes o 32 bits (64 bits es cada vez más frecuente). Byte y short son un poco más ineficientes, pero pueden ahorrar espacio.


He probado el rendimiento entre byte e int.
Con valores int:

class Program { private int a,b,c,d,e,f; public Program() { a = 1; b = 2; c = (a + b); d = (a - b); e = (b / a); f = (c * b); } static void Main(string[] args) { int max = 10000000; DateTime start = DateTime.Now; Program[] tab = new Program[max]; for (int i = 0; i < max; i++) { tab[i] = new Program(); } DateTime stop = DateTime.Now; Debug.WriteLine(stop.Subtract(start).TotalSeconds); } }

Con valores de byte:

class Program { private byte a,b,c,d,e,f; public Program() { a = 1; b = 2; c = (byte)(a + b); d = (byte)(a - b); e = (byte)(b / a); f = (byte)(c * b); } static void Main(string[] args) { int max = 10000000; DateTime start = DateTime.Now; Program[] tab = new Program[max]; for (int i = 0; i < max; i++) { tab[i] = new Program(); } DateTime stop = DateTime.Now; Debug.WriteLine(stop.Subtract(start).TotalSeconds); } }

Aquí el resultado:
byte: 3.57s 157mo, 3.71s 171mo, 3.74s 168mo con CPU ~ = 30%
int: 4.05s 298mo, 3.92s 278mo, 4.28 294mo con CPU ~ = 27%
Conclusión:
el byte usa más la CPU pero le cuesta a la memoria y es más rápido (tal vez porque hay menos byte para asignar)


La adición no está definida para los bytes. Así que son echados a int para la adición. Esto es cierto para la mayoría de las operaciones matemáticas y bytes. (note que así es como solía ser en idiomas más antiguos, supongo que es cierto hoy).


La tercera línea de su fragmento de código:

byte z = x + y;

en realidad significa

byte z = (int) x + (int) y;

Por lo tanto, no hay ninguna operación + en bytes, los bytes se convierten primero en enteros y el resultado de la adición de dos enteros es un entero (32 bits).


Las respuestas que indican alguna ineficiencia al agregar bytes y truncar el resultado a un byte son incorrectas. Los procesadores x86 tienen instrucciones específicamente diseñadas para operaciones de enteros en cantidades de 8 bits.

De hecho, para los procesadores x86 / 64, las operaciones de 32 bits o de 16 bits son menos eficientes que las operaciones de 64 bits u 8 bits debido al byte de prefijo del operando que se tiene que decodificar. En máquinas de 32 bits, realizar operaciones de 16 bits conlleva la misma penalización, pero todavía hay códigos de operación dedicados para operaciones de 8 bits.

Muchas arquitecturas RISC tienen similares instrucciones nativas de eficiencia de palabra / byte. Aquellos que generalmente no tienen una longitud de bit-store de conversión y conversión a valor firmado.

En otras palabras, esta decisión debe haberse basado en la percepción de para qué sirve el tipo de byte, no debido a las ineficiencias subyacentes del hardware.


Mi sospecha es que C # en realidad está llamando al operator+ definido en int (que devuelve un int menos que esté en un bloque checked ), y implícitamente convierte sus bytes / shorts en ints . Es por eso que el comportamiento parece inconsistente.


Recuerdo que una vez leí algo de Jon Skeet (no puedo encontrarlo ahora, seguiré buscando) sobre cómo el byte en realidad no sobrecarga al operador +. De hecho, al agregar dos bytes como en su muestra, cada byte se está convirtiendo implícitamente en un int. El resultado de eso es obviamente un int. Ahora en cuanto a POR QUÉ se diseñó de esta manera, esperaré a que el propio Jon Skeet publique :)

EDIT: ¡Lo encontré! Gran información sobre este mismo tema here .


Pensé que había visto esto antes en alguna parte. De este artículo, The Old New Thing :

Supongamos que vivimos en un mundo de fantasía donde las operaciones en ''byte'' resultaron en ''byte''.

byte b = 32; byte c = 240; int i = b + c; // what is i?

En este mundo de fantasía, el valor de i sería 16! ¿Por qué? Como los dos operandos del operador + son bytes, la suma "b + c" se calcula como un byte, lo que da como resultado 16 debido al desbordamiento de enteros. (Y, como señalé anteriormente, el desbordamiento de enteros es el nuevo vector de ataque de seguridad).

EDIT : Raymond está defendiendo, esencialmente, el enfoque C y C ++ tomó originalmente. En los comentarios, defiende el hecho de que C # adopta el mismo enfoque, debido a la compatibilidad con versiones anteriores del lenguaje.