piero borrelli
Tengo unas dudas sobre la conversión de tipos, me podrías explicar que pasa en una expresión como esta:
unsigned int u = 10;
int a = -42;
std::cout << u - a << std::endl;
Aquí sé que el resultado será 52 si aplico las reglas cuando tenemos dos operadores matemáticos. Pero me pregunto qué sucede cuando el compilador para convertir a un valor sin firmar crea un temporal de tipo sin firmar, ¿qué sucede después? La expresión ahora debería ser 10 -4294967254.
AnT apoya a Rusia
En términos simples, si mezcla tipos del mismo rango (en la secuencia de int
, long int
, long long int
), el tipo sin firmar “gana” y los cálculos se realizan dentro de ese tipo sin firmar. El resultado es del mismo tipo sin firmar.
Si mezcla tipos de rango diferente, el tipo de rango más alto “gana”, si puede representar todos los valores del tipo de rango más bajo. Los cálculos se realizan dentro de ese tipo. El resultado es de ese tipo.
Finalmente, si el tipo de clasificación más alta no puede representar todos los valores del tipo de clasificación más baja, entonces se usa la versión sin firmar del tipo de clasificación más alta. El resultado es de ese tipo.
En su caso, mezcló tipos del mismo rango (int
y unsigned int
), lo que significa que toda la expresión se evalúa dentro de unsigned int
tipo. La expresión, como dijiste correctamente, ahora es 10 - 4294967254
(para 32 bits int
). Los tipos sin signo obedecen las reglas de la aritmética módulo con 2^32
(4294967296
) como el módulo. Si calcula cuidadosamente el resultado (que se puede expresar aritméticamente como 10 - 4294967254 + 4294967296
), resultará como se esperaba 52
.
-
lo siento, me perdí, cuando la expresión se convierte en: unsigned int temporal = 10 – 4294967254 (está bien, lo he entendido) pero no puedo entender por qué la expresión se convierte en 10 – 4294967254 + 4294967296 (por qué agregas a la expresión el módulo aritmético ? ).
–Piero Borrelli
02/09/2014 a las 13:37
-
@Piero Borrelli: Una forma de calcular el
modulo N
equivalente a un valor negativoV
es agregarN
tantas veces como sea necesario (V + N
,V + 2N
,V + 3N
y así sucesivamente) hasta que llegue al primer valor no negativo. En el caso de operaciones aditivas de C++, un resultado matemáticamente negativo necesita que se agregue el valor del módulo solo una vez para llegar al resultado sin signo adecuado.– AnT apoya a Rusia
2 de septiembre de 2014 a las 14:12
-
@Piero Borrelli: Por supuesto, esta es una regla puramente aritmética. El compilador no tiene que hacer nada de eso. No tiene que preocuparse por eso en absoluto. Si los valores negativos se representan a través del complemento a 2, una simple reinterpretación de esa representación como sin signo proporciona inmediatamente el resultado correcto.
– AnT apoya a Rusia
02/09/2014 a las 16:17
-
¿Puedes definir lo que quieres decir con “rango”? C ++ no usa el rango de esa manera, lo que hace que esta respuesta sea ambigua en el mejor de los casos, y sin sentido en el peor.
– Adrián
18 de noviembre de 2019 a las 15:59
-
@Adrian: En realidad, lo hace. Me refiero al concepto de rango de conversión de enterostal como se utiliza en la descripción de conversiones aritméticas habituales. La descripción en mi respuesta no es la cita exacta de la norma, ya que está destinada a adaptarse al caso específico de
u - a
de la pregunta original.– AnT apoya a Rusia
18 de noviembre de 2019 a las 16:28
Betsabé
1) Debido a las reglas estándar de promoción, el signed
tipo a
es ascendido a un unsigned
escriba antes de la resta. Esa promoción ocurre de acuerdo con esta regla (C++ estándar 4.7/2):
Si el tipo de destino no tiene signo, el valor resultante es el menor entero sin signo congruente con el entero de origen (módulo 2n donde n es el número de bits utilizados para representar el tipo sin signo).
algebraicamente a
se convierte en un número positivo muy grande y ciertamente mayor que u
.
2) u - a
es un temporal anónimo y será un no firmado tipo. (Puede verificar esto escribiendo auto t = u - a
e inspeccionar el tipo de t
en su depurador). Matemáticamente, este será un número negativo, pero en la conversión implícita al tipo sin firmar, se invoca una regla envolvente similar a la anterior.
En resumen, las dos operaciones de conversión tienen efectos iguales y opuestos y el resultado será 52. En la práctica, el compilador podría optimizar todas estas conversiones.
aquí está el código de desmontaje dice:
primero se pone -42
a su complemento y hacer la operación sub. entonces el resultado es 10 + 42
0x0000000000400835 <+8>: movl $0xa,-0xc(%rbp)
0x000000000040083c <+15>: movl $0xffffffd6,-0x8(%rbp)
0x0000000000400843 <+22>: mov -0x8(%rbp),%eax
0x0000000000400846 <+25>: mov -0xc(%rbp),%edx
0x0000000000400849 <+28>: sub %eax,%edx
0x000000000040084b <+30>: mov %edx,%eax
-
En general, el código desensamblado no puede servir como una fuente significativa para comprender la semántica a nivel de lenguaje. La generación de código es una función unidireccional. No es posible “rastrearlo”. es decir, averiguar qué intentaba hacer el compilador mirando el código generado.
– AnT apoya a Rusia
1 de septiembre de 2014 a las 16:23
@dandan78 Quiero entender cómo funciona el compilador en el subyacente
–Piero Borrelli
1 de septiembre de 2014 a las 15:47
@ dandan78: a veces eso puede ser ingenuo; especialmente si algún aspecto del cálculo no está definido.
– Betsabé
1 de septiembre de 2014 a las 15:48
Paso 1: Obtenga una copia de C++ o C Standard (los últimos borradores son gratuitos) y verifíquelo. Paso 2: Decides que nunca podrás recordar las reglas y evitar ese tipo de cosas en el futuro.
– gnasher729
1 de septiembre de 2014 a las 16:05
@gnasher729: +1, muy buen consejo.
– Betsabé
1 de septiembre de 2014 a las 16:16
@ dandan78: Probablemente alrededor de 9 de cada 10 conceptos erróneos de los usuarios de C/C++ se originan al intentar, ver y luego malinterpretar lo que se vio.
– AnT apoya a Rusia
1 de septiembre de 2014 a las 16:27