Herramientas de usuario

Herramientas del sitio


curso-cpp:mas-tipos

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anterior Revisión previa
Próxima revisión
Revisión previa
curso-cpp:mas-tipos [2017/03/21 23:44]
santo [Observaciones sobre el tamaño de los distintos tipos]
curso-cpp:mas-tipos [2017/11/26 22:39] (actual)
guty [Tipo de un literal entero]
Línea 1: Línea 1:
-======= Más tipos de datos básicos de C++ =======+========= Más tipos de datos básicos de C++ =========
  
-===== Tipos enteros =====+====== Tipos enteros ​======
  
-En C++ existen múltiples tipos de datos **enteros**. El más común de ellos es el tipo ''​int'',​ que permite almacenar números entre $-2^31$ y $2^31-1$ , inclusive. Si alguna operación da por resultado números fuera de este rango de valores, obtendremos al trabajar con int resultados incorrectos.+En C++ existen múltiples tipos de datos **enteros**. El más común de ellos es el tipo ''​int'',​ que permite almacenar números entre $-2^{31}$ y $2^{31}-1$ , inclusive. Si alguna operación da por resultado números fuera de este rango de valores, obtendremos al trabajar con int resultados incorrectos.
  
 Similarmente,​ existen en C++ otros tipos de datos enteros fundamentales,​ pero de distinto **tamaño**:​ Los ''​int''​ tienen un tamaño de 32 **bits** (dígitos binarios), o 4 bytes, y eso además de definir el espacio de memoria RAM que ocupa cada variable de tipo ''​int'',​ limita el rango de valores que estos pueden representar. Similarmente,​ existen en C++ otros tipos de datos enteros fundamentales,​ pero de distinto **tamaño**:​ Los ''​int''​ tienen un tamaño de 32 **bits** (dígitos binarios), o 4 bytes, y eso además de definir el espacio de memoria RAM que ocupa cada variable de tipo ''​int'',​ limita el rango de valores que estos pueden representar.
Línea 9: Línea 9:
 En C++ existen otras variables enteras de diversos tamaños: En C++ existen otras variables enteras de diversos tamaños:
  
-   * ''​char'':​ Entero de 8 bits, entre $-2^7$ y $2^7-1$ inclusive, es decir, entre -128 y 127 inclusive. Ya lo hemos utilizado cuando trabajamos con caracteres, pues un caracter en C++ se representa directamente mediante un número, que es su código [[https://​en.wikipedia.org/​wiki/​ASCII|ASCII]]. +   * ''​char'':​ Entero de 8 bits, entre $-2^7$ y $2^7-1$ inclusive, es decir, entre $-128$127inclusive. Ya lo hemos utilizado cuando trabajamos con caracteres, pues un caracter en C++ se representa directamente mediante un número, que es su código [[https://​en.wikipedia.org/​wiki/​ASCII|ASCII]]. 
-   * ''​short'':​ Entero de 16 bits, entre $-2^15 = -32768$ y $2^15-1 = 32767$ inclusive. +   * ''​short'':​ Entero de 16 bits, entre $-2^{15= -32768$ y $2^{15}-1 = 32767$ inclusive. 
-   * ''​int'':​ Entero de 32 bits, entre $-2^31 = -2147483648$ y $2^31-1=2147483647$ inclusive. +   * ''​int'':​ Entero de 32 bits, entre $-2^{31= -2147483648$ y $2^{31}-1=2147483647$ inclusive. 
-   * ''​long long'':​ Entero de 64 bits, entre $-2^63=-9223372036854775808$ y $2^63 - 1=9223372036854775807$ inclusive.+   * ''​long long'':​ Entero de 64 bits, entre $-2^{63}=-9223372036854775808$ y $2^{63- 1=9223372036854775807$ inclusive.
  
 Para referencia, las máximas potencias de 10 que entran en el rango de cada tipo son respectivamente:​ Para referencia, las máximas potencias de 10 que entran en el rango de cada tipo son respectivamente:​
Línea 22: Línea 22:
 Todos estos tipos se usan de la misma manera que ''​int'',​ y solo cambia la cantidad de memoria que utilizan estas variables y el rango de valores posibles. Por ejemplo es perfectamente válido lo siguiente: Todos estos tipos se usan de la misma manera que ''​int'',​ y solo cambia la cantidad de memoria que utilizan estas variables y el rango de valores posibles. Por ejemplo es perfectamente válido lo siguiente:
  
-<​code>​+<​code ​cpp>
 short x = 32; short x = 32;
 int y = 1000; int y = 1000;
Línea 30: Línea 30:
 En las operaciones aritméticas,​ como regla general el tamaño del resultado de una operación es el máximo tamaño de sus operandos, es decir que si sumamos ''​short''​ e ''​int'',​ obtendremos ''​int'',​ y si sumamos ''​int''​ con ''​long long''​ obtendremos ''​long long''​. Esto es independiente del resultado de la operación, y solo depende de los **tipos** involucrados. En las operaciones aritméticas,​ como regla general el tamaño del resultado de una operación es el máximo tamaño de sus operandos, es decir que si sumamos ''​short''​ e ''​int'',​ obtendremos ''​int'',​ y si sumamos ''​int''​ con ''​long long''​ obtendremos ''​long long''​. Esto es independiente del resultado de la operación, y solo depende de los **tipos** involucrados.
  
-===== Tipo de una literal entero =====+===== Tipos enteros sin signo ===== 
 + 
 +Todos los anteriores permitían representar números negativos y positivos. Si bien casi nunca los usaremos, existen versiones de los anteriores en las cuales solamente se permiten números **no negativos**:​ estas se obtienen agregando ''​unsigned''​ (del inglés: "sin signo"​) al comienzo del tipo correspondiente. Así podemos obtener los siguientes tipos: 
 + 
 +   * ''​unsigned char'':​ Entero de 8 bits, entre $0$ y $2^8-1 = 255$ inclusive (Es por esto que en [[https://​es.wikipedia.org/​wiki/​The_Legend_of_Zelda_(videojuego)|The Legend of Zelda]], el máximo número de "​Rupies"​ que se pueden tener es exactamente 255. Estas primeras consolas de videojuegos eran **de 8 bits**). 
 +   * ''​unsigned short'':​ Entero de 16 bits, entre $0$ y $2^{16}-1 = 65535$ inclusive. 
 +   * ''​unsigned int''​ (equivalente a ''​unsigned''​ directamente sin aclarar ''​int''​):​ Entero de 32 bits, entre $0$ y $2^{32}-1=4294967295$ inclusive. 
 +   * ''​unsigned long long'':​ Entero de 64 bits, entre $0$ y $2^{64} - 1=18446744073709551615$ inclusive. 
 + 
 +Es decir, las versiones ''​unsigned''​ ocupan la misma memoria que sus correspondientes con signo, no permiten negativos, y a cambio llegan hasta números aproximadamente el doble de grandes como límite superior. La tabla de potencias de diez máximas representables es igual que antes, excepto que en ''​unsigned long long''​ ahora entra el número $10^{19}$, mientras que en ''​long long''​ solo se puede representar hasta $10^{18}$. 
 + 
 +===== Tipo de un literal entero =====
  
 Cuando escribimos un número directamente en el código, como por ejemplo ''​x = y + 33;'',​ cabe preguntarse:​ ¿De qué tamaño es ese 33? Esto es importante para las cuentas intermedias,​ pues ese tipo define el tamaño del resultado. Por ejemplo, si hacemos ''​long long x = y + 1000000000;'',​ donde ''​y''​ es de tipo ''​int'',​ el resultado "​matemático"​ de la cuenta siempre entrará en el rango de ''​long long'',​ pero... ¿Es para la computadora el resultado de la cuenta un ''​long long''?​ Cuando escribimos un número directamente en el código, como por ejemplo ''​x = y + 33;'',​ cabe preguntarse:​ ¿De qué tamaño es ese 33? Esto es importante para las cuentas intermedias,​ pues ese tipo define el tamaño del resultado. Por ejemplo, si hacemos ''​long long x = y + 1000000000;'',​ donde ''​y''​ es de tipo ''​int'',​ el resultado "​matemático"​ de la cuenta siempre entrará en el rango de ''​long long'',​ pero... ¿Es para la computadora el resultado de la cuenta un ''​long long''?​
Línea 42: Línea 53:
 Similar problema tendremos si hacemos ''​long long x = y + z'',​ siendo tanto ''​y''​ como ''​z''​ variables de tipo ''​int''​. Podemos solucionarlo **convirtiendo** una de ellas a ''​long long'':​ ''​long long x = y + (long long)(z)'',​ de forma análoga a lo que hacíamos con el ''​LL''​ para los literales enteros. Similar problema tendremos si hacemos ''​long long x = y + z'',​ siendo tanto ''​y''​ como ''​z''​ variables de tipo ''​int''​. Podemos solucionarlo **convirtiendo** una de ellas a ''​long long'':​ ''​long long x = y + (long long)(z)'',​ de forma análoga a lo que hacíamos con el ''​LL''​ para los literales enteros.
  
-**El caso más común** donde esto ocurre es en la expresión: ''​1 << i''​ (que es común si se trabaja con [[cpp-avanzado:​bitmask|máscaras ​de bits]] de 64 bits): El resultado de esta operación será de tipo ''​int'',​ que no es lo que queremos si estamos trabajando valores de 64 bits. ''​1LL << i''​ resuelve por completo este problema.+**El caso más común** donde esto ocurre es en la expresión: ''​1 << i''​ (que es común si se trabaja con [[cpp-avanzado:​operaciones-de-bits|operaciones ​de bits]] ​con números ​de 64 bits): El resultado de esta operación será de tipo ''​int'',​ que no es lo que queremos si estamos trabajando valores de 64 bits. ''​1LL << i''​ resuelve por completo este problema.
  
 ===== Reglas prácticas para el manejo de tipos enteros ===== ===== Reglas prácticas para el manejo de tipos enteros =====
Línea 57: Línea 68:
 Hemos mencionado aquí los tamaños para el caso del compilador gcc para PC, que es probablemente el más utilizado en competencias de programación. Sin embargo, el lenguaje C++ tiene la particularidad de que **no garantiza el tamaño exacto de los tipos enteros**, que pueden variar entre distintos compiladores del lenguaje. Hemos mencionado aquí los tamaños para el caso del compilador gcc para PC, que es probablemente el más utilizado en competencias de programación. Sin embargo, el lenguaje C++ tiene la particularidad de que **no garantiza el tamaño exacto de los tipos enteros**, que pueden variar entre distintos compiladores del lenguaje.
  
-Por ejemplo, existen compiladores para 64 bits en los cuales ''​int''​ es un tipo de 64 bits como ''​long long''​. Si usamos compiladores "​antiguos"​ para el sistema operativo DOS (Como el clásico [[https://​en.wikipedia.org/​wiki/​Turbo_C%2B%2B|Turbo C++]]), ''​int''​ será más chico y tendrá solamente 16 bits, ya que ese era el tamaño normal de operadores ​con los que las computadoras podían trabajar ​rápidamente.+Por ejemplo, existen compiladores para 64 bits en los cuales ''​int''​ es un tipo de 64 bits como ''​long long''​. Si usamos compiladores "​antiguos"​ para el sistema operativo DOS (Como el clásico [[https://​en.wikipedia.org/​wiki/​Turbo_C%2B%2B|Turbo C++]]), ''​int''​ será más chico y tendrá solamente 16 bits, ya que ese era el tamaño normal de los números ​con los que las computadoras podían trabajar ​eficientemente. 
 + 
 +====== Tipos de punto flotante ====== 
 + 
 +Siempre hemos trabajado con números enteros, que en computación es el caso más común de todos (y especialmente,​ en competencias de programación como la IOI y la OIA). 
 + 
 +Sin embargo, a veces es necesario trabajar con números decimales fraccionarios,​ especialmente al realizar cómputo científico,​ simulaciones o videojuegos,​ donde aparecen cálculos de física y química. Daremos aquí una introducción completamente básica, y [[cpp-avanzado:​punto-flotante|aquí]] se puede ver en cambio una descripción más completa y avanzada de la aritmética de punto flotante. 
 + 
 +Existen en C++ fundamentalmente 3 tipos de punto flotante disponibles. El más común, que es el **recomendado** y que utilizaremos casi siempre, es **double**. El tipo ''​double''​ utiliza 8 bytes (64 bits) para representar un número con coma. La precisión de double es de 15 dígitos decimales, que suelen ser más que suficientes para todos los cálculos que nos interesan. 
 + 
 +El tipo se utiliza en cuentas igual que hicimos con los enteros, pero se pueden usar números con coma (que en C++ se escriben siempre con ''​.'',​ y nunca con '',''​):​ 
 + 
 +<code cpp> 
 +double x = 0.2; 
 +double y = x * 5.3; 
 +double z = y / (x-0.72); 
 +cout << z << " " << x << endl; 
 +</​code>​ 
 + 
 +Este programa muestra por pantalla ''​-2.03846 0.2''​. Notemos que a diferencia de lo que ocurre con enteros, la división ''/''​ se realiza automáticamente con coma cuando estamos trabajando con ''​doubles''​. 
 + 
 +Es muy común querer mostrar una cierta cantidad **fija** de decimales, y no que esto lo decida el programa arbitrariamente como ocurrió en el ejemplo. Esto se puede hacer incluyendo ''#​include <​iomanip>'',​ y agregando una línea al programa antes de utilizar ''​cout''​ para mostrar los números: 
 + 
 +<code cpp> 
 +double x = 0.2; 
 +double y = x * 5.3; 
 +double z = y / (x-0.72); 
 +cout << fixed << setprecision(7);​ 
 +cout << z << " " << x << endl; 
 +</​code>​ 
 + 
 +Cambiando el ''​7''​ por otro número, elegimos cuántos decimales queremos mostrar. El ejemplo anterior genera la siguiente salida por pantalla: 
 + 
 +<​code>​ 
 +-2.0384615385 0.2000000000 
 +</​code>​ 
 + 
 +Una característica de los números de punto flotante es que **no dan resultados exactos**. Si bien tienen una precisión de 15 dígitos y normalmente los resultados son muy buenos generalmente,​ incluso para operaciones muy sencillas, **no se puede asumir que los resultados que dan serán exactos**, sino que debemos considerar siempre que tienen un pequeño error. 
 + 
 +Esto queda claro con el siguiente ejemplo: 
 + 
 +<code cpp> 
 +cout << fixed << setprecision(3);​ 
 +for (double x = 0.1; x < 1.0; x += 0.1) 
 +    cout << x << endl; 
 +</​code>​ 
 + 
 +Que muestra por pantalla: 
 + 
 +<​code>​ 
 +0.100 
 +0.200 
 +0.300 
 +0.400 
 +0.500 
 +0.600 
 +0.700 
 +0.800 
 +0.900 
 +1.000 
 +</​code>​ 
 + 
 +Que no es lo que esperamos si suponemos que las operaciones son **exactas**:​ El último número que vemos es 1, que no debería haberse mostrado pues pedimos seguir solo si ''​x < 1''​. 
 + 
 +Esto ocurre porque a diferencia de lo que pasa con enteros, las cuentas tienen pequeños errores, y en realidad el último número no es 1.000 sino que es un número apenas más pequeño, que cumple ''​x < 1''​. Podemos ver esto si aumentamos la precisión: mostrando 20 cifras decimales vemos 
 + 
 +<​code>​ 
 +0.10000000000000000555 
 +0.20000000000000001110 
 +0.30000000000000004441 
 +0.40000000000000002220 
 +0.50000000000000000000 
 +0.59999999999999997780 
 +0.69999999999999995559 
 +0.79999999999999993339 
 +0.89999999999999991118 
 +0.99999999999999988898 
 +</​code>​ 
 + 
 +Vemos que, si bien los errores relativos son extremadamente pequeños (aparecen recién en la cifra 15), **siempre están ahí**, así que no podemos tratar a los números como absolutamente exactos en nuestro programa. 
 + 
 +=== Otros tipos de punto flotante === 
 + 
 +Además de ''​double'',​ existen otros tipos de punto flotante en C++ que se usan de idéntica manera: 
 + 
 +   * ''​float'':​ Es un número de punto flotante que ocupa solamente 32 bits. Tiene **mucha** menos precisión que ''​double'',​ por lo cual lo recomendamos solamente cuando estemos ante una emergencia y sea absolutamente necesario ahorrar memoria o acelerar un poquito el tiempo de cálculo de la computadora. 
 +   * ''​long double'':​ Es un número de punto flotante de 80 bits, con más precisión que double (unas 18 cifras). En general no es necesario (aunque en algunos casos podría serlo), y es más lento y ocupa más memoria.
curso-cpp/mas-tipos.1490139842.txt.gz · Última modificación: 2017/03/21 23:44 por santo