tipos tipo tabla que programacion float ejemplos datos dato c assembly

tabla - tipos de datos en c#



¿Cómo maneja el compilador c el entero sin signo y con signo? ¿Por qué el código de ensamblaje para operación aritmética sin signo y firmado es el mismo? (5)

Estoy leyendo el libro: CS-APPe2. C tiene un tipo int sin signo y firmado y en la mayoría de las arquitecturas usa una aritmética de dos complementos para implementar el valor con signo; pero después de aprender un código de ensamblaje, encontré que muy pocas instrucciones distinguen entre unsigned y signed. Entonces mi pregunta es:

  1. ¿Es responsabilidad del compilador diferenciar firmado y sin firmar? Si es así, ¿cómo lo hace?

  2. ¿Quién implementa la aritmética de dos complementos? ¿La CPU o el compilador?

Agrega más información:

Después de aprender algunas instrucciones más, en realidad hay algunas que diferencian entre firmado y sin firmar, como setg, seta, etc. Además, CF y OF se aplican a unsigned y respectivamente. Pero la mayoría de las instrucciones aritméticas enteras tratan sin firma y con la misma firma, por ejemplo

int s = a + b

y

unsigned s = a + b

generar la misma instrucción.

Entonces, al ejecutar ADD sd , ¿debería la CPU tratar s & d sin firmar o firmado? ¿O es irrelevante, porque el patrón de bits de ambos resultados es el mismo y la tarea del compilador consiste en convertir el resultado del patrón de bits subyacente en no firmado o firmado?

PD: estoy usando x86 y gcc


En muchos casos, no hay diferencia en el nivel de la máquina entre las operaciones con signo y sin signo, y es simplemente una cuestión de interpretación del patrón de bits. Por ejemplo, considere la siguiente operación de palabras de 4 bits:

Binary Add Unsigned 2''s comp ---------- -------- -------- 0011 3 3 + 1011 + 11 - 5 ------- -------- -------- 1110 14 -2 ------- -------- --------

El patrón binario es el mismo para la operación con signo y sin signo. Tenga en cuenta que la resta es simplemente la adición de un valor negativo. Cuando se realiza una operación SUB, el operando de la derecha se complementa en dos (bits de inversión e incremento) y luego se agrega (el circuito responsable de la ALU es un sumador ); no en el nivel de instrucción que usted entiende, sino en el nivel lógico, aunque sería posible implementar una máquina sin una instrucción SUB y aun así realizar restas aunque en dos instrucciones en lugar de una.

Hay algunas operaciones que requieren instrucciones diferentes según el tipo, y es responsabilidad del compilador generar el código apropiado en términos generales, pueden aplicarse variaciones arquitectónicas.


No es necesario diferenciar las entradas firmadas y no firmadas para la mayoría de las operaciones aritméticas / lógicas. A menudo solo es necesario tener en cuenta el signo al imprimir, cero / firmar extendiendo o comparando valores. De hecho, la CPU no sabe nada sobre el tipo de valor. Un valor de 4 bytes es solo una serie de bits, no tiene ningún significado a menos que el usuario indique que se trata de un flotante, una matriz de 4 caracteres, un int sin firmar o un int firmado, etc. Por ejemplo, al imprimir una variable char según el tipo y las propiedades de salida indicadas, imprimirá el carácter, entero sin signo o entero con signo. Es responsabilidad del programador mostrarle al compilador cómo tratar ese valor y luego el compilador emitirá la instrucción correcta necesaria para procesar el valor.


Esto también me molestó por mucho tiempo. No llegué a saber cómo funciona el compilador como un programa mientras se entregan sus valores predeterminados e instrucciones implícitas. Pero mi búsqueda de una respuesta me llevó a las siguientes conclusiones:

El mundo real utiliza enteros con signo solamente, desde el descubrimiento de números negativos. esa es la razón por la cual int se considera un entero con signo por defecto en el compilador. Ignoro por completo la aritmética de números sin signo, ya que es inútil.

La CPU no tiene ninguna pista de enteros con y sin signo. Simplemente conoce los bits: 0 y 1. La forma de interpretar su salida depende de usted como programador de ensamblaje. Eso hace que la programación en ensamblador sea tediosa. Tratar números enteros (firmados y no firmados) involucró mucho control de banderas. Es por eso que se desarrollaron idiomas de alto nivel. el compilador quita todo el dolor.

Cómo funciona el compilador es un aprendizaje muy avanzado. Acepté que en este momento está más allá de mi comprensión. Esta aceptación me ayudó a seguir adelante en mi curso.

En arquitectura x86:

las instrucciones add y sub modifican flags en el registro eflags. Estas banderas se pueden usar junto con las instrucciones adc y sbb para construir aritmética con mayor precisión. En tal caso, movemos el tamaño de los números al registro ecx. El número de veces que se lleva a cabo la instrucción de bucle es el mismo que el tamaño de los números en bytes.

La subinstrucción toma el complemento de 2 del sustraendo, lo agrega al minuendo, invierte el acarreo. Esto se hace en hardware (implementado en el circuito). La subinstrucción ''activa'' un circuito diferente. Después de usar la instrucción secundaria, el programador o compilador verifica el CF. Si es 0, el resultado es positivo y el destino tiene el resultado correcto. Si es 1, el resultado es negativo y el destino tiene el complemento de 2 del resultado. Normalmente, el resultado se deja en complemento a 2 y se lee como un número firmado, pero las instrucciones NOT e INC se pueden usar para cambiarlo. La instrucción NOT realiza el complemento de 1 del operando, luego el operando se incrementa para obtener el complemento de 2.

Cuando un programador ha planificado leer el resultado de un add o subinstruccion como un numero firmado, debera mirar la bandera OF. Si se establece en 1, el resultado es incorrecto. Debería firmar, extender los números antes de ejecutar la operación entre ellos.


Es bastante fácil. Las operaciones como la suma y la resta no necesitan ningún ajuste para los tipos con signo en la aritmética de complemento a dos. Simplemente realiza un experimento mental e imagina un algoritmo usando solo las siguientes operaciones matemáticas:

  • incrementar en uno
  • decremento por uno
  • comparar con cero

La adición es simplemente tomar los elementos uno a uno de un montón y ponerlos en el otro montón hasta que el primero esté vacío. La resta está tomando de ambos a la vez, hasta que el restado está vacío. En aritmética modular, simplemente trata el valor más pequeño como el valor más grande más uno y funciona. El complemento a dos es solo una aritmética modular donde el valor más pequeño es negativo.

Si desea ver alguna diferencia, le recomiendo que pruebe operaciones que no son seguras con respecto al desbordamiento. Un ejemplo es la comparación ( a < b ).

¿Es responsabilidad del compilador diferenciar entre firmado y sin firmar? Si es así, ¿cómo lo hace?

Generando un ensamblaje diferente siempre que sea necesario.

¿Quién implementa la aritmética de dos complementos? ¿La CPU o el compilador?

Es una pregunta difícil. El complemento a dos es probablemente la forma más natural de trabajar con enteros negativos en una computadora. La mayoría de las operaciones para el complemento de dos con desbordamiento son las mismas que para los enteros sin signo con desbordamiento. El signo se puede extraer de un solo bit. La comparación se puede realizar conceptualmente por sustracción (que es signo-agnóstico), extracción de bit de signo y comparación a cero.

Sin embargo, son las características aritméticas de la CPU las que le permiten al compilador producir cálculos en dos complementos.

sin signo s = a + b

Tenga en cuenta que el modo más se calcula aquí no depende del tipo de resultado. Insead depende de los tipos de variables a la derecha del signo igual.

Entonces, al ejecutar ADD sd, ¿debería la CPU tratar s & d sin firmar o firmado?

Las instrucciones de la CPU no conocen los tipos, solo los usa el compilador. Además, no hay diferencia entre agregar dos números sin firmar y agregar dos números con signo. Sería estúpido tener dos instrucciones para la misma operación.


Mucho se ha dicho sobre su primera pregunta, pero me gustaría decir algo sobre su segunda pregunta:

¿Quién implementa la aritmética de dos complementos? ¿La CPU o el compilador?

El estándar C no requiere que los números negativos tengan un complemento de dos, no define en absoluto cómo el hardware expresa los números negativos. La tarea del compilador es traducir tu código C en instrucciones de CPU que hagan lo que tu código solicite. Entonces, si el compilador de C creará código para la aritmética de dos complementos o no depende únicamente del hecho si su CPU usa aritmética de dos complementos o no. El compilador debe saber cómo funciona la CPU y crear código en consecuencia. Entonces la respuesta correcta a esa pregunta es: La CPU.

Si su CPU usaba una representación complementaria de uno, entonces un compilador de C para esa CPU emitiría instrucciones de complemento de uno. Por otro lado, un compilador de C puede emular el soporte para números negativos en una CPU que no conoce números negativos en absoluto. Como el complemento de dos le permite ignorar si un número está firmado o sin firmar para muchas operaciones, esto no es demasiado difícil de hacer. En ese caso, habría sido el compilador que implementó la aritmética de complemento a dos. Esto también podría hacerse en una CPU que tenga una representación para números negativos, pero ¿por qué el compilador debería hacer eso y no solo usar la forma nativa que entiende la CPU? Entonces no hará eso a menos que tenga que hacerlo.