ejemplos c++ c math limits

c++ - ejemplos - ¿Cómo saber minúsculamente(INT_MAX, abs(INT_MIN))?



sqrt c++ (6)

En C99 y superior, INT_MAX .

Quoth la especificación:

Para los tipos enteros con signo, los bits de la representación del objeto se dividirán en tres grupos: bits de valor, bits de relleno y el bit de signo. No es necesario que haya ningún relleno; el signo firmado no debe contener ningún relleno. Habrá exactamente un bit de signo. Cada bit que sea un bit de valor tendrá el mismo valor que el mismo bit en la representación del objeto del tipo sin signo correspondiente (si hay M bits de valor en el tipo con signo y N en el tipo sin signo, entonces M ≤ N). Si el bit de signo es cero, no afectará el valor resultante. Si el bit de signo es uno, el valor se modificará de una de las siguientes maneras:

  • el valor correspondiente con el bit de signo 0 es negado (signo y magnitud);
  • el bit de signo tiene el valor - (2 ^ M) (complemento de dos);
  • el bit de signo tiene el valor - (2 ^ M - 1) (complemento de unos).

(Sección 6.2.6.2 de http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf )

¿Cómo puedo saber de forma portátil el más pequeño de INT_MAX y abs ( INT_MIN )? (Ese es el valor absoluto matemático de INT_MIN , no una llamada a la función abs .)

Debería ser igual que INT_MAX en la mayoría de los sistemas, pero estoy buscando una forma más portátil.


En la mayoría de los sistemas, abs (INT_MIN) no está definido. Por ejemplo, en máquinas típicas de 32 bits, INT_MAX = 2 ^ 31 - 1, INT_MIN = - 2 ^ 31 y abs (INT_MIN) no pueden ser 2 ^ 31.


Mientras que el valor típico de INT_MIN es -2147483648, y el valor típico de INT_MAX es 2147483647, no está garantizado por el estándar. TL; DR: el valor que está buscando es INT_MAX en una implementación conforme. Pero calcular min(INT_MAX, abs(INT_MIN)) no es portátil.

Los valores posibles de INT_MIN e INT_MAX

INT_MIN e INT_MAX están definidos por el Anexo E (Límites de implementación) 1 (C estándar, C ++ hereda esto):

El contenido del encabezado se da a continuación, en orden alfabético. Las magnitudes mínimas que se muestran se reemplazarán por magnitudes definidas por la implementación con el mismo signo. Todos los valores deben ser expresiones constantes adecuadas para su uso en las directivas #if preprocessing. Los componentes se describen más detalladamente en 5.2.4.2.1.

[...]

#define INT_MAX +32767

#define INT_MIN -32767

[...]

El estándar requiere que el tipo int sea ​​un tipo entero que puede representar el rango [INT_MIN, INT_MAX] (sección 5.2.4.2.1.).

Entonces, 6.2.6.2. (Tipos enteros, otra vez parte del estándar C), entra en juego y lo restringe aún más a lo que conocemos como complemento de dos o unos :

Para los tipos enteros con signo, los bits de la representación del objeto se dividirán en tres grupos: bits de valor, bits de relleno y el bit de signo. No es necesario que haya ningún relleno; el signo firmado no debe contener ningún relleno. Habrá exactamente un bit de signo. Cada bit que sea un bit de valor tendrá el mismo valor que el mismo bit en la representación del objeto del tipo sin signo correspondiente (si hay M bits de valor en el tipo con signo y N en el tipo sin signo, entonces M ≤ N). Si el bit de signo es cero, no afectará el valor resultante. Si el bit de signo es uno, el valor se modificará de una de las siguientes maneras:

- el valor correspondiente con el bit de signo 0 es negado (signo y magnitud);

- el bit de signo tiene el valor - (2M) (complemento de dos);

- el bit de signo tiene el valor - (2M - 1) (complemento de unos).

Sección 6.2.6.2. también es muy importante relacionar la representación del valor de los tipos enteros con signo con la representación del valor de sus hermanos sin signo.

Esto significa que obtienes el rango [-(2^n - 1), (2^n - 1)] o [-2^n, (2^n - 1)] , donde n es típicamente 15 o 31.

Operaciones en tipos enteros con signo

Ahora, para lo segundo: operaciones en tipos enteros con signo, que dan como resultado un valor que no está dentro del rango [INT_MIN, INT_MAX] , el comportamiento no está definido. Esto está explícitamente establecido en C ++ por el Párrafo 5/4:

Si durante la evaluación de una expresión, el resultado no está matemáticamente definido o no está en el rango de valores representables para su tipo, el comportamiento no está definido.

Para C, 6.5 / 5 ofrece un pasaje muy similar:

Si se produce una condición excepcional durante la evaluación de una expresión (es decir, si el resultado no está definido matemáticamente o no está en el rango de valores representables para su tipo), el comportamiento no está definido.

Entonces, ¿qué sucede si el valor de INT_MIN resulta ser menor que el negativo de INT_MAX (por ejemplo, -32768 y 32767, respectivamente)? Cálculo -(INT_MIN) no estará definido, lo mismo que INT_MAX + 1 .

Por lo tanto, debemos evitar calcular un valor que may no esté en el rango de [INT_MIN, INT_MAX] . Afortunado, INT_MAX + INT_MIN está siempre en ese rango, ya que INT_MAX es un valor estrictamente positivo e INT_MIN un valor estrictamente negativo. De ahí INT_MIN < INT_MAX + INT_MIN < INT_MAX .

Ahora podemos verificar si INT_MAX + INT_MIN es igual a, menor que o mayor que 0.

`INT_MAX + INT_MIN` | value of -INT_MIN | value of -INT_MAX ------------------------------------------------------------------ < 0 | undefined | -INT_MAX = 0 | INT_MAX = -INT_MIN | -INT_MAX = INT_MIN > 0 | cannot occur according to 6.2.6.2. of the C standard

Por lo tanto, para determinar el mínimo de INT_MAX y -INT_MIN (en el sentido matemático), el siguiente código es suficiente:

if ( INT_MAX + INT_MIN == 0 ) { return INT_MAX; // or -INT_MIN, it doesn''t matter } else if ( INT_MAX + INT_MIN < 0 ) { return INT_MAX; // INT_MAX is smaller, -INT_MIN cannot be represented. } else // ( INT_MAX + INT_MIN > 0 ) { return -INT_MIN; // -INT_MIN is actually smaller than INT_MAX, may not occur in a conforming implementation. }

O, para simplificar:

return (INT_MAX + INT_MIN <= 0) ? INT_MAX : -INT_MIN;

Los valores en un operador ternario solo se evaluarán si es necesario. Por lo tanto, -INT_MIN se ha evaluado (por lo tanto, no puede producir UB), o es un valor bien definido.

O, si quieres una afirmación:

assert(INT_MAX + INT_MIN <= 0); return INT_MAX;

O, si quieres eso en tiempo de compilación:

static_assert(INT_MAX + INT_MIN <= 0, "non-conforming implementation"); return INT_MAX;

Hacer operaciones enteras correctas (es decir, si la corrección es importante)

Si está interesado en la aritmética de enteros seguros, eche un vistazo a mi implementación de operaciones enteras seguras . Si desea ver los patrones (en lugar de esta larga salida de texto) sobre qué operaciones fallan y cuáles tienen éxito, elija esta demostración .

Dependiendo de la arquitectura, puede haber otras opciones para garantizar la corrección, como la opción de gcc -ftrapv .


-INT_MAX es representable como int en todos los dialectos C y C ++, hasta donde yo sé. Por lo tanto:

-INT_MAX <= INT_MIN ? -INT_MIN : INT_MAX


abs(INT_MIN) invocará comportamiento indefinido. Standard dice

7.22.6.1 Las funciones abs , labs y llabs :

Las funciones abs , labs y llabs calculan el valor absoluto de un entero j . Si el resultado no se puede representar, el comportamiento no está definido.

Pruebe esto en su lugar:
Convierta INT_MIN en unsignrd int . Como -ve números no pueden representarse como unsigned int , INT_MAX se convertirá en UINT_MAX + 1 + INT_MIN .

#include <stdio.h> #include <stdlib.h> unsigned min(unsigned a, unsigned b) { return a < b ? a : b; } int main(void) { printf("%u/n", min(INT_MAX, INT_MIN)); }


INT_MAX + INT_MIN < 0 ? INT_MAX : -INT_MIN

Editado para agregar una explicación: por supuesto, la dificultad es que -INT_MIN o abs(INT_MIN) no estarán definidos si -INT_MIN es demasiado grande para caber en un int . Entonces, necesitamos alguna forma de verificar si este es el caso. La condición INT_MAX + INT_MIN < 0 prueba si -INT_MIN es mayor que INT_MAX . Si es así, entonces INT_MAX es el más pequeño de los dos valores absolutos. De lo contrario, INT_MAX es el mayor de los dos valores absolutos, y -INT_MIN es la respuesta correcta.