visual ushort sirve que para long c# types integer-arithmetic

c# - sirve - ¿Por qué ushort+ushort es igual a int?



ushort c# (5)

De la especificación de lenguaje C #:

7.3.6.2 Promociones numéricas binarias La promoción numérica binaria se produce para los operandos de los operadores predefinidos +, -, *, /,%, &, |, ^, ==,! =,>, <,> = Y <= binarios . La promoción numérica binaria convierte implícitamente ambos operandos a un tipo común que, en el caso de los operadores no relacionales, también se convierte en el tipo de resultado de la operación. La promoción de números binarios consiste en aplicar las siguientes reglas, en el orden en que aparecen aquí:

· Si cualquiera de los operandos es de tipo decimal, el otro se convierte a tipo decimal, o se produce un error de tiempo de enlace si el otro operando es de tipo float o double.

· De lo contrario, si cualquiera de los operandos es de tipo doble, el otro operando se convierte en tipo doble.

· De lo contrario, si cualquiera de los operandos es de tipo float, el otro operando se convierte en tipo float.

· De lo contrario, si cualquiera de los operandos es de tipo ulong, el otro se convierte al tipo ulong, o se produce un error de tiempo de vinculación si el otro operando es de tipo sbyte, short, int o long.

· De lo contrario, si cualquiera de los operandos es de tipo largo, el otro operando se convierte en tipo long.

· De lo contrario, si cualquiera de los operandos es de tipo uint y el otro es de tipo sbyte, short o int, ambos operandos se convierten a tipo long.

· De lo contrario, si cualquiera de los operandos es de tipo uint, el otro operando se convierte al tipo uint.

· De lo contrario, ambos operandos se convierten al tipo int.

Anteriormente, hoy intentaba agregar dos usuarios y noté que tenía que devolver el resultado a nuestro error. Pensé que podría haberse convertido en una uint (¿para evitar un posible desbordamiento involuntario?), Pero para mi sorpresa fue un int (System.Int32).

¿Hay alguna razón inteligente para esto o tal vez porque int se ve como el tipo entero "básico"?

Ejemplo:

ushort a = 1; ushort b = 2; ushort c = a + b; // <- "Cannot implicitly convert type ''int'' to ''ushort''. An explicit conversion exists (are you missing a cast?)" uint d = a + b; // <- "Cannot implicitly convert type ''int'' to ''uint''. An explicit conversion exists (are you missing a cast?)" int e = a + b; // <- Works!

Edición: como dice la respuesta de GregS, la especificación de C # dice que ambos operandos (en este ejemplo ''a'' y ''b'') deben convertirse a int. Me interesa la razón subyacente por la que esto es parte de la especificación: ¿por qué la especificación de C # no permite operaciones directamente en valores de ushort?


La respuesta simple y correcta es "porque la Especificación del lenguaje C # lo dice".

Claramente no estás contento con esa respuesta y quieres saber "por qué lo dice". Estás buscando "fuentes creíbles y / o oficiales", eso va a ser un poco difícil. Estas decisiones de diseño se tomaron hace mucho tiempo, 13 años son muchas vidas de perros en ingeniería de software. Fueron hechos por los "veteranos" como Eric Lippert los llama, se han movido hacia cosas más grandes y mejores y no publican respuestas aquí para proporcionar una fuente oficial.

Sin embargo, se puede inferir, con el riesgo de ser meramente creíble. Cualquier compilador administrado, como C # ''s, tiene la restricción de que necesita generar código para la máquina virtual .NET. Las reglas para las cuales se describen con cuidado (y de manera bastante legible) en la especificación CLI. Es la especificación Ecma-335, puede descargarla gratis desde aquí .

Pase a la partición III, capítulo 3.1 y 3.2. Describen las dos instrucciones de IL disponibles para realizar una adición, add y add.ovf . Haga clic en el enlace a la Tabla 2, "Operaciones numéricas binarias", que describe qué operandos están permitidos para esas instrucciones IL. Tenga en cuenta que hay sólo unos pocos tipos enumerados allí. Byte y corto, así como todos los tipos sin firmar faltan. Solo se permite int, long, IntPtr y coma flotante (flotante y doble). Con restricciones adicionales marcadas con una x, no puede agregar un int a un largo por ejemplo. Estas restricciones no son completamente artificiales, se basan en cosas que puede hacer razonablemente eficientes con el hardware disponible.

Cualquier compilador administrado tiene que lidiar con esto para generar un IL válido. Eso no es difícil, simplemente convierta el ushort a un tipo de valor más grande que está en la tabla, una conversión que siempre es válida. El compilador de C # selecciona int, el siguiente tipo más grande que aparece en la tabla. O, en general, convierta cualquiera de los operandos al siguiente tipo de valor más grande para que ambos tengan el mismo tipo y cumplan con las restricciones de la tabla.

Ahora hay un nuevo problema, sin embargo, un problema que impulsa a los programadores de C # bastante loco. El resultado de la adición es del tipo promovido. En tu caso será int. Entonces, agregar dos valores de ushort de, digamos, 0x9000 y 0x9000 tiene un resultado int perfectamente válido: 0x12000. El problema es que es un valor que no encaja en un ushort. El valor se desbordó . Pero no se desbordó en el cálculo de IL, solo se desborda cuando el compilador intenta volver a meterlo en un ushort. 0x12000 se trunca a 0x2000. Un valor diferente desconcertante que solo tiene sentido cuando se cuenta con 2 o 16 dedos, no con 10.

Notable es que la instrucción add.ovf no trata este problema. Es la instrucción que se debe utilizar para generar automáticamente una excepción de desbordamiento. Pero no es así, el cálculo real en los ints convertidos no se desbordó.

Aquí es donde entra en juego la verdadera decisión de diseño. Los veteranos aparentemente decidieron que simplemente truncar el resultado internacional para usarlo era una fábrica de errores. Ciertamente lo es. Decidieron que tiene que reconocer que sabe que la adición puede desbordarse y que está bien si sucede. Lo hicieron su problema, principalmente porque no sabían cómo hacerlo suyo y aún así generan código eficiente. Tienes que lanzar. Sí, eso es enloquecedor, estoy seguro de que tampoco querías ese problema.

Bastante notable es que los diseñadores de VB.NET tomaron una solución diferente al problema. Realmente lo hicieron su problema y no pasaron el dinero. Puede agregar dos UShorts y asignarlo a un UShort sin un lanzamiento. La diferencia es que el compilador VB.NET en realidad genera IL extra para verificar la condición de desbordamiento. Eso no es un código barato, hace que cada adición corta sea 3 veces más lenta. Pero de lo contrario, la razón que explica por qué Microsoft mantiene dos idiomas que tienen capacidades muy similares.

En pocas palabras: estás pagando un precio porque usas un tipo que no es muy bueno para las arquitecturas de CPU modernas. Lo que en sí mismo es una razón realmente buena para usar uint en lugar de ushort. Quitarle algo de tracción es difícil, necesitará muchos de ellos antes de que el costo de manipularlos supere los ahorros de memoria. No solo por la especificación CLI limitada, un núcleo x86 toma un ciclo de CPU adicional para cargar un valor de 16 bits debido al byte de prefijo de operando en el código de la máquina. No estoy seguro de que ese sea el caso hoy en día, solía volver cuando todavía prestaba atención a los ciclos de conteo. Hace un perro hace un año.

Tenga en cuenta que puede sentirse mejor con estos modelos feos y peligrosos al permitir que el compilador C # genere el mismo código que genera el compilador VB.NET. Así que obtienes una OverflowException cuando el elenco resultó ser imprudente. Use Proyecto> Propiedades> pestaña Generar> botón Avanzado> marque la casilla de verificación "Verificar desbordamiento / subflujo aritmético". Sólo para la construcción de depuración. Por qué esta casilla de verificación no está activada automáticamente por la plantilla del proyecto es otra pregunta muy desconcertante por cierto, una decisión que se tomó hace mucho tiempo.


No hay ninguna razón por la que se pretende. Esto es solo un efecto o la aplicación de las reglas de resolución de sobrecarga que establecen que la primera sobrecarga para cuyos parámetros hay una conversión implícita que se ajusta a los argumentos, se usará esa sobrecarga.

Esto se establece en la Especificación C #, sección 7.3.6 de la siguiente manera:

La promoción numérica no es un mecanismo distinto, sino un efecto de aplicar la resolución de sobrecarga a los operadores predefinidos.

Sigue ilustrando con un ejemplo:

Como ejemplo de promoción numérica, considere las implementaciones predefinidas del operador binario *:

operador int * (int x, int y);

operador uint * (uint x, uint y);

operador largo * (largo x, largo y);

operador ulong * (ulong x, ulong y);

operador flotante * (float x, float y);

doble operador * (doble x, doble y);

operador decimal * (decimal x, decimal y);

Cuando se aplican reglas de resolución de sobrecarga (§7.5.3) a este conjunto de operadores, el efecto es seleccionar el primero de los operadores para los cuales existen conversiones implícitas de los tipos de operandos. Por ejemplo, para la operación b * s, donde b es un byte y s es corto, la resolución de sobrecarga selecciona al operador * (int, int) como el mejor operador.


Tu pregunta es, de hecho, un poco complicada. La razón por la que esta especificación es parte del lenguaje es ... porque tomaron esa decisión cuando crearon el idioma. Sé que suena como una respuesta decepcionante, pero así es como es.

Sin embargo, la respuesta real probablemente involucraría muchas decisiones de contexto en el día 1999-2000. Estoy seguro de que el equipo que hizo C # tuvo debates bastante sólidos sobre todos los detalles del idioma.

  • ...
  • C # pretende ser un lenguaje de programación simple, moderno, de propósito general y orientado a objetos.
  • La portabilidad del código fuente es muy importante, al igual que la portabilidad del programador, especialmente para aquellos programadores que ya están familiarizados con C y C ++.
  • El apoyo a la internacionalización es muy importante.
  • ...

La cita anterior es de Wikipedia C #

Todos esos objetivos de diseño podrían haber influido en su decisión. Por ejemplo, en el año 2000, la mayoría del sistema ya era nativo de 32 bits, por lo que podrían haber decidido limitar el número de variables más pequeñas, ya que de todos modos se convertirá a 32 bits al realizar operaciones aritméticas. Que es generalmente más lento.

En ese punto, podrías preguntarme; Si hay conversión implícita en esos tipos, ¿por qué los incluyeron de todos modos? Bueno, uno de sus objetivos de diseño, como se cita arriba, es la portabilidad.

Por lo tanto, si necesita escribir una envoltura de C # alrededor de un programa antiguo de C o C ++, es posible que necesite ese tipo para almacenar algunos valores. En ese caso, esos tipos son bastante útiles.

Esa es una decisión que Java no tomó. Por ejemplo, si escribe un programa Java que interactúa con un programa C ++ de la forma en que se le reciben los valores de corto plazo, bueno, Java solo es corto (que está firmado), por lo que no puede asignar fácilmente uno a otro y esperar valores correctos.

Te dejo apostar, el siguiente tipo disponible que podría recibir dicho valor en Java es int (32 bits, por supuesto). Acabas de duplicar tu memoria aquí. Lo que podría no ser un gran problema, en lugar de eso, tiene que crear una instancia de una matriz de 100 000 elementos.

De hecho, debemos recordar que esas decisiones se toman mirando el pasado y el futuro para proporcionar una transferencia sin problemas de una a otra.

Pero ahora siento que me estoy desviando de la pregunta inicial.

Por lo tanto, su pregunta es buena y, con suerte, pude darle algunas respuestas, aunque sé que probablemente no es lo que quería escuchar.

Si lo desea, puede incluso leer más sobre la especificación de C #, enlaces a continuación. Hay alguna documentación interesante que podría ser interesante para usted.

Tipos integrales

Los operadores comprobados y no controlados.

Tabla de conversiones numéricas implícitas

Por cierto, creo que probablemente deberías recompensarlo por habib-osu , ya que proporcionó una respuesta bastante buena a la pregunta inicial con un enlace adecuado. :)

Saludos


ushort x = 5, y = 12;

La siguiente instrucción de asignación producirá un error de compilación, porque la expresión aritmética en el lado derecho del operador de asignación evalúa a int de forma predeterminada.

ushort z = x + y; // Error: conversion from int to ushort

http://msdn.microsoft.com/en-us/library/cbf1574z(v=vs.71).aspx

EDITAR:
En caso de operaciones aritméticas en ushort, los operandos se convierten a un tipo que puede contener todos los valores. Para que se pueda evitar el desbordamiento. Los operandos pueden cambiar en el orden de int, uint, long y ulong. Consulte la Especificación de lenguaje C # En este documento, vaya a la sección 4.1.5 Tipos integrales (alrededor de la página 80 en el documento de Word). Aquí encontrarás:

Para los operadores binarios +, -, *, /,%, &, ^, |, ==,! =,>, <,> =, Y <=, los operandos se convierten al tipo T, donde T es el primero de int, uint, long y ulong que pueden representar completamente todos los valores posibles de ambos operandos . La operación se realiza luego utilizando la precisión del tipo T, y el tipo del resultado es T (o bool para los operadores relacionales). No se permite que un operando sea de tipo largo y que el otro sea de tipo ulong con los operadores binarios.

Eric Lipper ha declarado en una question

La aritmética nunca se hace en pantalones cortos en C #. La aritmética se puede hacer en entres, puntos, largos y largos, pero la aritmética nunca se hace en pantalones cortos. Los cortos promueven a int y la aritmética se realiza en ints, porque, como dije antes, la gran mayoría de los cálculos aritméticos encajan en un int. La gran mayoría no cabe en un corto. La aritmética corta es posiblemente más lenta en el hardware moderno que está optimizado para los ints, y la aritmética corta no ocupa menos espacio; Se va a hacer en pulgadas o largos en el chip.