c

c - ¿Qué representa "-1" en el rango de valores para unsigned int yigned int?



(5)

Estoy aprendiendo C y tengo una pregunta tonta con respecto al "-1" en el rango de valores para unsigned int yigned int. Parece que no puedo encontrar una explicación para eso en ningún lado.

El siguiente párrafo explica el rango de datos. Sin embargo, no explica el "-1". ¿Qué significa "-1" representa / significa? ¿Es -1 porque omite 0 y 0 no tiene valor?

En enteros de 32 bits, un entero sin signo tiene un rango de 0 a 2 ^ 32 -1 = 0 a 4,294,967,295 o aproximadamente 4 mil millones. La versión firmada va de -2 ^ 31 -1 a 2 ^ 31, que es –2,147,483,648 a 2,147,483,647 o aproximadamente -2 mil millones a +2 mil millones. El rango es el mismo, pero se desplaza en la recta numérica.


¿Dónde encontraste este párrafo incorrecto? Parece ser el complemento de 2 pero tiene el -1 en el lugar equivocado.

Para implementaciones de C que usan el complemento de uno o enteros con signo / magnitud, el rango es simétrico alrededor de cero (con patrones de 2 bits que representan 0 , por lo que el rango positivo y el rango negativo son del mismo tamaño).

Básicamente, en la actualidad, nada usa eso, pero el estándar ISO C especifica que los enteros con signo son binarios y usan el complemento de dos, el complemento de uno o el signo / magnitud.

En twos''-complement (casi universal en estos días), el rango de valores representables que usan n bits es [- 2 n-1 , 2 n-1 - 1]. Un patrón de bits (todos los bits cero) representa el valor cero . Cada bit tiene un valor posicional de 2^i , excepto el último que tiene un valor posicional de -2^(n-1) .

El patrón de bits con todos los bits establecidos representa -1 porque sum(2^i, i=0..n-1) es uno menor que 2^n .

Con solo el bit de signo establecido, obtenemos el número más negativo : -INT_MIN es desbordamiento de signo (comportamiento indefinido) porque no es representable como int ; requiere un número entero más amplio. O con el -INT_MIN = INT_MIN , -INT_MIN = INT_MIN . Esta es la "anomalía del complemento a 2". https://en.wikipedia.org/wiki/Two%27s_complement#Most_negative_number

Puede evitar ampliar si está haciendo una operación de valor absoluto: por ejemplo
unsigned abs = i >= 0 ? i : -(unsigned)i;

(La conversión de un valor negativo a unsigned en C tiene un comportamiento bien definido de reducción de módulo hasta que esté en el rango representable. En C esto es independiente de la codificación de entero con signo; lo que importa es el valor . Entonces (uint8_t)-1 es siempre 255. Para el complemento de 2, solo copia el patrón de bits. Para el signo / magnitud o el complemento de uno, una implementación de C tendría que hacer algunas matemáticas para convertir de firmado a firmado. Tenga en cuenta que hice esto antes de la negación, lo que significa 0 - i con el envoltorio sin firmar habitual).


Además de la excelente explicación de @ Yunnosch sobre números sin signo, casi todas las computadoras modernas usan el "complemento de dos" para representar enteros binarios con signo. En el complemento a dos, el bit más significativo se usa como el "bit de signo" y los bits son el complemento del valor absoluto del número + 1. Entonces, para el ejemplo de 3 bits, mientras que el rango para valores sin signo es de 0 a 7, el rango para valores con signo es -4 a 3:

100 : -4 101 : -3 110 : -2 111 : -1 000 : 0 001 : 1 010 : 2 011 : 3

Observe que para los números con signo, el rango de números negativos es uno mayor que el rango de números positivos. Eso es porque, mientras que en la teoría de números, 0 no es ni positivo ni negativo, en representación binaria, 0 tiene que ser negativo o positivo. Debido a que tiene el bit más significativo eliminado, 0 es parte del dominio de número positivo, por lo que queda un número positivo menos disponible.


Considere los valores que puede lograr con 2 bits:

00 : 0 01 : 1 10 : 2 11 : 3

Hay 4 de ellos, 2 a la potencia de 2.
Pero el valor más alto no es 4, es 3.
El valor más alto es 2 a la potencia de 2 menos 1. Es decir, en su representación

2 ^ 2-1
o 2 2 -1

Agregue un poco y obtendrá el doble del número, al agregar

100 : 4 101 : 5 110 : 6 111 : 7

Número total 8, pero número más alto 7.

Entonces el "-1" se debe a que siempre se usa el primero del total de 2 n para 0,
el segundo se usa para 1, el tercero se usa para 2.
Al final (2 n ) el uno no está disponible para 2 n , ya se usa para 2 n -1.


Para un tipo entero sin signo, el valor -1 está fuera de rango y no se puede representar en una variable de ese tipo. Si intenta asignar -1 a un unsigned int produce una conversión de acuerdo con las reglas del estándar C.

La conversión de un valor con signo a un tipo entero sin signo se especifica en la sección 6.3.1.3p2 del estándar C :

De lo contrario, si el nuevo tipo no tiene signo, el valor se convierte agregando o restando repetidamente uno más que el valor máximo que se puede representar en el nuevo tipo hasta que el valor esté en el rango del nuevo tipo. 60 60

...

60) Las reglas describen la aritmética en el valor matemático, no el valor de un tipo dado de expresión

Suponiendo, como en su ejemplo, que unsigned int tiene un rango de valores de 0 a 4,294,967,295, el valor -1 se convierte agregando -1 + 4,294,967,296 = 4,294,967,295. Tenga en cuenta que esta conversión se produce independientemente de cómo se representan los números negativos en el sistema dado. Es lo mismo para el cumplido de dos, el cumplido de uno o el signo y la magnitud.

Tenga en cuenta que esto significa que la representación del valor convertido no necesita ser la misma que la representación de -1.

Usando un tipo de 4 bits como ejemplo, la conversión del valor -1 a un tipo sin signo da como resultado el valor 15. La representación de estos números es la siguiente:

sign-and magnitude ones'' complement two''s complement -1 (signed) 1001 1110 1111 15 (unsigned) 1111 1111 1111

Mientras que en el caso del complemento a dos el resultado de la conversión mantiene la misma representación, cambia en los otros dos casos. Para el complemento de uno, la representación de -1 es la misma que para 14, y para el signo y la magnitud, la representación de -1 es la misma que para 9.

Entonces, lo que otras respuestas han descrito con respecto al complemento de dos es muy probablemente cómo lo hacen esas implementaciones (es decir, reinterpretando la representación de -1 como un valor sin signo), sin embargo, desde la perspectiva del lenguaje C como una máquina abstracta, lo que he descrito es la única forma correcta de realizar esta conversión.


n bits pueden contener 2 n valores diferentes. (El primer bit puede tener dos valores * el segundo bit puede tener dos valores * el tercer bit puede tener dos valores * ...)

Por ejemplo, 3 bits pueden contener 2 3 = 8 valores diferentes.

000 001 010 011 100 101 110 111

Si cada patrón de bits representa un número entero, entonces un número entero de n bits puede representar 2 n números enteros diferentes. Por ejemplo,

  • Podría representar los enteros de 0 a 2 n -1 inclusive
    (porque (2 n -1) - (0) + 1 = 2 n valores diferentes).

    Por ejemplo,

    000 0 001 1 010 2 011 3 100 4 101 5 110 6 111 7

  • Podría representar los enteros de -2 n-1 a 2 n-1 -1 inclusive
    (porque (2 n-1 -1) - (-2 n-1 ) + 1 = 2 n valores diferentes).

    Por ejemplo,

    100 -4 101 -3 110 -2 111 -1 000 0 001 1 010 2 011 3

Puede asignar cualquier significado a estos valores, pero los rangos indicados anteriormente son entendidos por las máquinas de complemento a dos para enteros sin signo y enteros con signo respectivamente. [1]

  1. En una ones''-complement , hay dos formas de escribir cero (0000 ... 0000 2 y 1000 ... 0000 2 ), por lo que el rango es solo -2 n-1 -1 a 2 n-1 -1 . Sin embargo, creo que todas las máquinas modernas son máquinas de twos''-complement .