sucesivas sucesiva restas resta recursividad por para niños mediante entera dfd como c++ unsigned integer-overflow arithmetic-expressions

c++ - sucesiva - Resta entre firmado y sin firmar seguido de división



division por restas sucesivas dfd (3)

int i1 = 20-80u; // -60

¡Esto tiene demonios sutiles! Los operandos son diferentes, por lo que es necesaria una conversión. Ambos operandos se convierten a un tipo común (un unsigned int , en este caso). El resultado, que será un gran valor unsigned int (60 menos que UINT_MAX + 1 si mis cálculos son correctos) se convertirá a un int antes de que se almacene en i1 . Dado que ese valor está fuera del rango de int , el resultado será una implementación definida, podría ser una representación de captura y, por lo tanto, podría causar un comportamiento indefinido cuando intente usarlo. Sin embargo, en su caso se convierte casualmente a -60 .

int i3 =(20-80u)/2; // 2147483618

Continuando con el primer ejemplo, supongo que el resultado de 20-80u sería 60 menos que UINT_MAX + 1 . Si UINT_MAX es 4294967295 (un valor común para UINT_MAX ), eso significaría que 20-80u es 4294967236 ... y 4294967236 / 2 es 2147483618.

En cuanto a i2 y los demás, no debería haber sorpresas. Siguen los cálculos matemáticos convencionales sin conversiones, truncamientos, desbordamientos u otros comportamientos definidos por la implementación.

Los siguientes resultados me hacen realmente confundido:

int i1 = 20-80u; // -60 int i2 = 20-80; // -60 int i3 =(20-80u)/2; // 2147483618 int i4 =(20-80)/2; // -30 int i5 =i1/2; // -30

  1. i3 parece calcularse como (20u-80u)/2 , en lugar de (20-80u)/2
  2. supuestamente i3 es lo mismo que i5 .

IIRC, una operación aritmética entre int sin signo y con signo producirá un resultado sin firmar.

Por lo tanto, 20 - 80u produce el resultado sin signo equivalente a -60 : si unsigned int es un tipo de 32 bits, el resultado es 4294967236.

Por cierto, la asignación de eso a i1 produce un resultado definido por la implementación porque el número es demasiado grande para ajustarse. Obtener -60 es típico, pero no está garantizado.


Los operadores aritméticos binarios realizarán las conversiones aritméticas habituales en sus operandos para llevarlos a un tipo común.

En el caso de i1 , i3 e i5 el tipo común estará sin signo int y, por lo tanto, el resultado también estará sin signo int . Los números sin firmar se ajustarán a través de la aritmética de módulo, por lo que al restar un valor sin firmar ligeramente mayor se obtendrá un número cercano al int max sin signo que no se puede representar con un int.

Entonces, en el caso de i1 , terminamos con una conversión definida por la implementación, ya que el valor no se puede representar. En el caso de i3 dividir por 2 devuelve el valor sin firmar al rango de int y, por lo tanto, terminamos con un valor int con signo grande después de la conversión.

Las secciones relevantes del borrador de norma de C ++ son las siguientes. Sección 5.7 [expr.add] :

Los operadores aditivos + y - agrupan de izquierda a derecha. Las conversiones aritméticas habituales se realizan para operandos de tipo aritmético o de enumeración.

Las conversiones aritméticas habituales están cubiertas en la sección 5 y dice:

Muchos operadores binarios que esperan operandos de tipo aritmético o de enumeración provocan conversiones y producen tipos de resultados de manera similar. El propósito es generar un tipo común, que también es el tipo del resultado. Este patrón se llama las conversiones aritméticas habituales, que se definen de la siguiente manera:

[...]

  • De lo contrario, si el operando que tiene un tipo entero sin signo tiene un rango mayor o igual al rango del tipo del otro operando, el operando con tipo entero con signo se convertirá al tipo del operando con tipo entero sin signo.

y para la conversión de un valor que no se puede representar para un tipo firmado, sección 4.7 [conv.integral] :

Si el tipo de destino está firmado, el valor no se modifica si se puede representar en el tipo de destino (y el ancho del campo de bits); de lo contrario, el valor está definido por la implementación.

y para enteros sin signo obedece el módulo aritmético sección 3.9.1 [basic.fundamental] :

Los enteros sin signo obedecerán las leyes del módulo aritmético 2n donde n es el número de bits en la representación del valor de ese tamaño particular de entero.48