¿Cuáles son los casos en que los tipos de datos 'uint' y 'short' se ajustan mejor que el estándar int (32)?

24

Entiendo las diferencias en capacidad y valores que pueden representar, pero parece que las personas siempre usan Int32independientemente de si es apropiado. Nadie parece usar la versión sin signo ( uint) aunque muchas veces se ajusta mejor, ya que describe un valor que no puede ser negativo (tal vez para representar una ID de un registro de base de datos). Además, nadie parece usarlo short/Int16independientemente de la capacidad requerida del valor.

Objetivamente, ¿hay casos en los que es mejor usar uinto, de short/Int16ser así, cuáles son?

Alternatex
fuente
13
La popularidad no siempre es una medida viable para evaluar las decisiones de diseño de software. El hecho de que una práctica sea popular no significa que sea una práctica adecuada para su aplicación en particular, o que incluso sea una buena práctica.
Robert Harvey
1
La respuesta corta, creo, es que los programadores se han acostumbrado a la semántica firmada y se inclinan a asumirlos, incluso cuando se trata de tipos sin signo (y por lo tanto, semántica sin signo). La mayoría de las personas asume que se trata de que el programador sea flojo o no tenga educación, pero el programador en cuestión puede, de hecho, ser muy educado y muy cuidadoso y quiere evitar dificultades sutiles. Si lo desea, eche un vistazo a soundsoftware.ac.uk/c-pitfall-unsigned y anteru.net/2010/05/17/736 .
Theodoros Chatzigiannakis
En un número sin signo, el signo es más nullque positivo o negativo. Si lo considera como algo que nunca puede ser negativo o siempre es positivo, se sorprenderá (y a menudo se enojará) con los resultados porque realmente no funciona de esa manera, especialmente cuando se compara o se resta a / de valores firmados.
Adam D. Ruppe
1
En mi experiencia, la mayoría de los programadores, que alguna vez han programado en lenguaje C, tienden a preocuparse por los bytes, aún en estos días, de GB de memoria y espacio de almacenamiento.
user1451111

Respuestas:

25

Sospecho que te estás refiriendo a una perspectiva coloreada por tus propias experiencias en la que no has trabajado con personas que usan los tipos integrales correctamente. Esto puede ser una ocurrencia común, pero según mi experiencia, la gente también los usa correctamente.

El beneficio es el espacio de memoria y el tiempo de CPU, posiblemente también el espacio de E / S dependiendo de si los tipos se envían alguna vez por cable o a un disco. Los tipos sin firmar le brindan comprobaciones del compilador para asegurarse de que no realizará ciertas operaciones que son imposibles, además de extender el rango disponible al tiempo que mantiene el tamaño más pequeño para un mayor rendimiento cuando sea necesario.

El uso correcto es el que esperaría: cada vez que sepa con certeza , puede usarlos permanentemente (no restrinja sin certeza o se arrepentirá más adelante).

  • Si está tratando de representar algo que nunca podría ser razonablemente negativo ( public uint NumberOfPeople), use un tipo sin signo.
  • Si está tratando de representar algo que nunca podría ser razonablemente mayor que 255 ( public byte DamagedToothCount), use un byte.
  • Si está tratando de representar algo que podría ser razonablemente mayor que 255, pero nunca un número significativo de miles , use un short ( public short JimmyHoffasBankBalance).
  • Si está tratando de representar algo que podría ser de cientos de miles, incluso de millones, pero es poco probable que llegue a miles de millones, use un int ( public int HoursSinceUnixEpoch).
  • Si sabe con certeza que este número puede tener un valor ilimitadamente grande o cree que puede tener varios miles de millones, pero no está seguro de cuántos miles de millones, es su mejor apuesta. Si el tiempo no es lo suficientemente grande, tiene un problema interesante y necesita comenzar a buscar números de precisión arbitrarios ( public long MyReallyGreatAppsUserCountThisIsNotWishfulThinkingAtAll).

Este razonamiento se puede utilizar para elegir entre tamaños de tipos y otros firmados, sin signo y variados, solo piense en las verdades lógicas de los datos que representa en la realidad.

Jimmy Hoffa
fuente
11
+1, aunque tengo que dejar en claro que los "números" de teléfono no son números sino cadenas de dígitos y, opcionalmente, formateo. Parece ser consciente de esto, pero no queremos dar un mal ejemplo, ¿verdad? Además, restringir arbitrariamente el rango de algún valor es un antipatrón miope, en inttodas partes, a menos que sepa con certeza que el dominio del problema realmente restringe el valor, ningún banco querría limitar las cuentas a 33K quid (y piense en la diversión cuando eso se desborda ...!).
amon
3
Nuevo objetivo de vida: sobregiro considerable que socava el tipo integral de mi cuenta bancaria.
recursion.ninja
11
Hay buenas razones para no usar tipos sin signo en ciertos lugares, por ejemplo, cuando la aritmética se mezcla entre con signo y sin signo. Consulte ¿Cuáles son las mejores prácticas con respecto a las entradas sin firmar? .
19
No estoy de acuerdo con el razonamiento aquí. Los tipos sin signo son a menudo un error debido a que las restas y las comparaciones son inesperadas si está acostumbrado a las entradas (funcionan de manera consistente pero no es "siempre positivo"). Los evitaría a menos que tenga una razón muy específica para usarlos. Además, ¿por qué el tamaño es importante para byte vs short vs int? A menudo ni siquiera ahorras espacio, ya que las estructuras rellenarán esos miembros o matrices a una cierta alineación. Usaría un byte solo si el tamaño es realmente importante (poco probable, especialmente para el código C # que he visto) o si desea específicamente envolver a 255 para algo.
Adam D. Ruppe
44
"El beneficio es el espacio de memoria y el tiempo de CPU" ... No veo ningún caso en el que los tipos pequeños realmente ahorren tiempo de CPU. Las operaciones enteras nunca son más rápidas de lo que son en los tipos del tamaño de la máquina , es decir, en lo que respecta a la CPU, también podría usar long. Por supuesto, el ahorro de memoria puede ahorrar tiempo indirectamente al mejorar la eficiencia de la línea de caché, etc., pero OTOH los problemas de alineación con tipos pequeños pueden indirectamente costar tiempo.
Leftaroundabout
16

Claro, hay casos en los que es mejor usar uinto shorto Int16. Cuando sepa que su rango de datos se ajustará a las restricciones de ese tipo de variable, entonces está bien usar ese tipo.

En entornos con memoria limitada o cuando se trata con grandes cantidades de objetos, puede tener sentido usar la variable de tamaño más pequeño. Por ejemplo, hay una diferencia significativa en el tamaño de un conjunto de millones de elementos de ints vs. shorts.

A menudo, eso no sucede en el código real por una o más de las siguientes razones:

  • Las restricciones de datos no se conocían de antemano
  • Existía la posibilidad de que las restricciones de datos no fueran sólidas o se supiera que probablemente cambiaran
  • Había una esperanza de reutilizar la función con un rango de datos más amplio
  • El desarrollador no se tomó el tiempo para pensar en las restricciones
  • El ahorro de memoria fue insignificante para justificar el uso de un tipo variable más pequeño

Hay muchas más razones posibles, pero se reducen a esto: el tiempo involucrado en decidir y usar un tipo de variable diferente no proporcionó suficiente beneficio para justificar hacerlo.


fuente
8

En C, en contextos que no involucran la promoción de enteros , se especificaron valores sin signo para comportarse como miembros de un anillo algebraico abstracto "envolvente" (por lo que para cualquier X e Y, XY producirá un valor único que, cuando se agrega a Y, producirá X ), mientras que los tipos enteros con signo se especificaban como comportándose como enteros cuando los cálculos se mantenían dentro de un cierto rango, y se les permitía hacer cualquier cosa cuando los cálculos iban más allá de eso. La semántica numérica en C #, sin embargo, es totalmente diferente. Cuando se encuentra dentro de un contexto numérico verificado, los tipos con signo y sin signo se comportan como enteros siempre que los cálculos permanezcan dentro del rango y se arrojen OverflowExceptioncuando no lo hacen; en un contexto no controlado, ambos se comportan como anillos algebraicos.

El único momento en el que generalmente vale la pena usar cualquier tipo de datos más pequeño que Int32cuando es necesario empacar o desempacar cosas para almacenamiento o transporte compacto. Si uno necesita almacenar 500 millones de números positivos, y todos estarán en el rango de 0 a 100, usar un byte cada uno en lugar de cuatro ahorrará 1.5 gigabytes de almacenamiento. Eso es un gran ahorro. Sin embargo, si una pieza de código necesita almacenar un total de un par de cientos de valores, hacer que cada uno de ellos sea un byte en lugar de cuatro ahorraría aproximadamente 600 bytes. Probablemente no valga la pena molestarse.

Con respecto a los tipos sin signo, las únicas veces que son realmente útiles son cuando se realiza el intercambio de información o cuando se subdividen los números en partes. Si, por ejemplo, uno necesita hacer cálculos matemáticos en enteros de 96 bits, probablemente será mucho más fácil realizar los cálculos en grupos de tres enteros de 32 bits sin signo, que en grupos de enteros con signo. De lo contrario, no hay muchas situaciones en las que el rango de un valor con signo de 32 o 64 bits sea inadecuado, pero sería suficiente el mismo tamaño de valor sin signo.

Super gato
fuente
4

En general, es una mala idea usar tipos sin signo porque se desbordan de formas desagradables. x = 5-6De repente es una bomba de tiempo en su código. Mientras tanto, los beneficios de los tipos sin signo se reducen a un solo bit adicional de precisión, y si ese bit lo vale para usted, seguramente debería usar un tipo más grande.

Hay casos de uso en los que un tipo más pequeño podría tener sentido, pero a menos que le preocupe el uso de la memoria o necesite empacar datos para la transmisión o la eficiencia de la caché o un puñado de otras preocupaciones, generalmente es el caso de que no sea beneficioso usar un tipo más pequeño . Además, en muchas arquitecturas, en realidad es más lento usar estos tipos para que puedan imponer un pequeño costo.

Jack Aidley
fuente
3
En C, el desbordamiento con signo es incluso peor que el desbordamiento sin signo (porque es un comportamiento indefinido, mientras que sin signo está especificado para pasar como un odómetro). OTOH, el overflow / underflow firmado es mucho menos común en la práctica que el underflow sin firmar.
Kevin
Es cierto, pero el desbordamiento firmado suele ser más obvio y predecible.
Jack Aidley
Yo por lo general de acuerdo, pero sí es necesario ser conscientes, por ejemplo, que los compiladores modernos pueden optimizar i+1>ien 1si iestá firmado, junto con toda una serie de otros comportamientos desagradable. El desbordamiento sin firmar puede causar un error en un caso de esquina. El desbordamiento firmado puede hacer que todo su programa no tenga sentido .
Kevin
@JackAidley Estoy bastante seguro de que lo que dices no tiene sentido, ya que 5-6 produce el mismo patrón de bits, sin importar si no está firmado o no.
Ingo
@Ingo: ¿con qué frecuencia miras los patrones de bits? Lo que importa es el significado del patrón de bits, no los bits que están activados o desactivados.
Jack Aidley
2

A menudo olvidado y posiblemente tangencial a su pregunta, cuando se trata específicamente con tipos .NET, es el cumplimiento de CLS . No todos los tipos están disponibles para todos los idiomas creados en .NET Framework.

Si escribe código para que lo consuman otros idiomas que no sean C # y desea que se garantice que ese código interopere con la mayor cantidad posible de idiomas .NET, debe restringir su uso de tipos a aquellos que cumplan con CLS.

Por ejemplo, las primeras versiones de VB.NET (7.0 y 7.1) no admitían enteros sin signo ( UInteger):

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

Los enteros sin signo no son compatibles con CLS y, por lo tanto, deben usarse con cuidado si no está seguro de quién será el consumidor de la biblioteca de su clase.

Kev
fuente