¿Está utilizando una buena práctica de desbordamiento de enteros sin signo?

El otro día estaba leyendo el C Standard y me di cuenta de que, a diferencia del desbordamiento de entero con signo (que no está definido), el desbordamiento de entero sin signo está bien definido. Lo he visto usado en una gran cantidad de códigos para máximos, etc. pero dado el vudú sobre el desbordamiento, ¿se considera esto una buena práctica de progtwigción? ¿Está de alguna manera inseguro? Sé que muchos lenguajes modernos como Python no lo admiten, sino que continúan ampliando el tamaño de los números grandes.

El desbordamiento de enteros sin signo (en forma de envolvente) se aprovecha rutinariamente en las funciones hash, y lo ha sido desde el punto de año.

Una forma en que podría pensar en un desbordamiento de enteros sin signo causando un problema es cuando se resta de un pequeño valor sin signo, lo que resulta en un gran valor positivo.

Consejos prácticos para desbordamientos de enteros:
http://www.gnu.org/software/hello/manual/autoconf/Integer-Overflow-Basics.html#Integer- Overflow-Basics

Para decirlo brevemente:

Es perfectamente legal / correcto / seguro utilizar un desbordamiento de entero sin signo como lo considere oportuno, siempre que preste atención y se adhiera a la definición (para cualquier propósito: optimización, algoritmos súper inteligentes, etc.)

Solo porque sepas las minucias de la norma no significa que la persona que mantiene tu código sí lo haga. Esa persona puede perder el tiempo preocupándose por esto mientras realiza la depuración más tarde, o tiene que buscar el estándar para verificar este comportamiento más adelante.

Claro, esperamos competencia con las características razonables de un lenguaje en un progtwigdor en funcionamiento, y diferentes compañías / grupos tienen una expectativa diferente sobre dónde se encuentra esa competencia razonable. Pero para la mayoría de los grupos esto parece ser demasiado esperar que la próxima persona lo sepa de la cabeza y no tenga que pensar en ello.

Si eso no fuera suficiente, es más probable que se encuentre con errores de comstackción cuando esté trabajando al margen de la norma. O peor, la persona que transfiere este código a una nueva plataforma puede toparse con ellos.

En resumen, ¡yo voto, no lo hagas!

Lo uso todo el tiempo para decir si es hora de hacer algo.

 UInt32 now = GetCurrentTime() if( now - then > 100 ) { // do something } 

Siempre y cuando compruebe el valor antes de “ahora” vueltas “luego”, está bien para todos los valores de “ahora” y “luego”.

EDIT: supongo que esto es realmente un underflow.

No confiaría en eso solo por razones de legibilidad. Va a depurar su código durante horas antes de averiguar dónde está restableciendo esa variable a 0.

Otro lugar donde el desbordamiento sin signo se puede usar de manera útil es cuando tiene que iterar hacia atrás desde un tipo sin firmar dado:

 void DownFrom( unsigned n ) { unsigned m; for( m = n; m != (unsigned)-1; --m ) { DoSomething( m ); } } 

Otras alternativas no son tan limpias. Intentar hacer m >= 0 no funciona a menos que cambies m a signed, pero entonces podrías estar truncando el valor de n – o peor – convirtiéndolo a un número negativo en la inicialización.

De lo contrario, tienes que hacer! = 0 o> 0 y luego hacer manualmente el caso 0 después del ciclo.

Dado que los números con signo en las CPU se pueden representar de diferentes maneras, el 99,999% de todas las CPU actuales utilizan la notación de complemento a dos. Dado que esta es la mayoría de las máquinas que existen, es difícil encontrar un comportamiento diferente, aunque el comstackdor podría comprobarlo (posibilidad de grasa). Sin embargo, las especificaciones C deben representar el 100% de los comstackdores, por lo que no han definido su comportamiento.

Entonces haría las cosas más confusas, que es una buena razón para evitarlo. Sin embargo, si tiene una buena razón (por ejemplo, un aumento de rendimiento del factor de 3 para una parte crítica del código), documente bien y úselo.

Está bien confiar en el desbordamiento siempre que sepa CUÁNDO ocurrirá …

Yo, por ejemplo, tuve problemas con la implementación C de MD5 cuando migraba a un comstackdor más reciente … El código esperaba un desbordamiento pero también esperaba 32 bits de entrada.

¡Con 64 bits los resultados fueron incorrectos!

Afortunadamente para eso están las pruebas automatizadas: descubrí el problema temprano, pero esta podría haber sido una verdadera historia de terror si hubiera pasado desapercibida.

Podrías discutir “pero esto sucede raramente”: sí, pero eso es lo que lo hace aún más peligroso. Cuando hay un error, todos sospechan del código escrito en los últimos días. Nadie sospecha que el código “solo funcionó durante años” y, por lo general, nadie sabe todavía cómo funciona …

Si lo usa sabiamente (bien comentado y legible), puede beneficiarse de ello teniendo un código más pequeño y más rápido.

siukurnin hace un buen punto, que necesita saber cuándo ocurrirán los desbordamientos. La forma más fácil de evitar el problema de portabilidad que describió es usar los tipos de enteros de ancho fijo de stdint.h. uint32_t es un entero de 32 bits sin signo en todas las plataformas y sistemas operativos, y no se comportará de manera diferente cuando se comstack para un sistema diferente.

Sugeriría siempre tener un reparto explícito cada vez que uno va a confiar en el envolvimiento de números sin firmar. De lo contrario, puede haber sorpresas. Por ejemplo, si “int” es de 64 bits, escriba un código como:

 UInt32 foo,bar; if ((foo-bar) < 100) // Report if foo is between bar and bar+99, inclusive) ... do something 

puede fallar, ya que "foo" y "bar" se promocionarían a enteros de 64 bits. Agregar un typecast a UInt32 antes de verificar el resultado contra 100 evitaría problemas en ese caso.

A propósito, creo que la única forma portátil de obtener directamente los 32 bits inferiores del producto de dos UInt32 es lanzar uno de los ints a un UInt64 antes de hacer el multiplicador. De lo contrario, el UInt32 podría convertirse a int64s firmados, con la multiplicación desbordante y produciendo resultados indefinidos.