- Conversión implícita de tipos
- Conversiones aritméticas de tipos
- Conversión explícita de tipos
Conversiones aritméticas de tipo
En el cálculo aritmético y en las expresiones de comparación a menudo se utilizan como operandos valores de distintos tipos. Para procesarlos correctamente es necesario llevar los tipos a un cierto «denominador común». El compilador intenta hacerlo sin la intervención del programador, a menos que éste haya especificado reglas de conversión explícitas (véase Conversión explícita de tipos). En este caso, el compilador, siempre que sea posible, intenta conservar la máxima precisión cuando se trata de números. En concreto, ello produce un aumento de la capacidad de los números enteros y de la transición de números enteros a reales (si intervienen).
La expansión de números enteros implica la conversión de bool, char, unsigned char, short, unsigned short a int (o unsigned int si int no es lo suficientemente grande para almacenar números específicos). Los valores grandes pueden convertirse a long y unsigned long.
Si el tipo de la variable no es capaz de almacenar el resultado del tipo que se obtuvo al evaluar la expresión, el compilador emitirá un aviso.
double d = 1.0;
|
La expresión para inicializar las variables x y y contiene el número real 1.0, por lo que los otros operandos (la constante 10 en este caso) se convierten a double, y el resultado de la división también será del tipo double. Sin embargo, el tipo de las variables es int, y por lo tanto se produce una conversión implícita al mismo.
El cálculo 1.0 / 10 lo realiza el compilador durante la compilación y por lo tanto obtiene una constante de tipo double (0.1) Por supuesto, en la práctica, es poco probable que la constante de inicialización supere el tamaño de la variable receptora. Por lo tanto, la advertencia del compilador «truncamiento de valor constante» puede considerarse extraña. Sólo muestra el problema de la forma más simplificada.
Sin embargo, como resultado de los cálculos basados en variables, también pueden producirse pérdidas de datos similares. El segunda aviso del compilador que vemos aquí («posible pérdida de datos debido a la conversión de tipos») se produce con mucha más frecuencia. Además, la pérdida es posible no sólo al convertir de tipo real a entero, sino también al revés.
double f = LONG_MAX; // truncation of constant value
|
Como sabemos, el tipo double no puede representar con precisión números enteros grandes (aunque su rango de valores válidos es mucho mayor que long).
Otro aviso que podemos encontrar debido a la falta de coincidencia de tipo es «sobrante de constante de enteros».
long m1 = 1000000000;
|
Las constantes de enteros en MQL5 tienen el tipo int, por lo que la multiplicación de millón por millón se realiza teniendo en cuenta el rango de este tipo, que es igual a INT_MAX (2147483647). El valor 1000000000000000000 causa un sobrante, y m3 obtiene el resto después de dividir este valor por el rango (encontrará más información al respecto en la barra lateral más abajo).
El hecho de que la variable receptora m3 sea del tipo long no significa que los valores de la expresión deban convertirse previamente a ella. Esto sólo ocurre en el momento de la asignación. Para que la multiplicación se realice según las reglas de long es necesario especificar de algún modo el tipo long directamente en la propia expresión. Esto puede hacerse con una conversión explícita o mediante variables. En particular, la obtención del mismo producto utilizando una variable m1 de tipo long (como m1 * m1) conduce al resultado correcto en m2.
Enteros con y sin signo
Los programas no siempre se escriben a la perfección, con protección frente a todos los fallos posibles. Por lo tanto, a veces ocurre que el número entero obtenido durante los cálculos no cabe en la variable del tipo entero seleccionado. Entonces, obtiene el resto de dividir este valor por el valor máximo (M) que puede escribirse en el número de bytes correspondiente (tamaño de tipo), más 1. Así, para tipos enteros con tamaños de 1 a 4 bytes, M + 1 es, respectivamente, 256, 65536, 4294967296 y 18446744073709551616.
Pero existe un matiz para los tipos con signo. Como sabemos, para los números con signo, el rango total de valores se divide aproximadamente a partes iguales entre áreas positivas y negativas. Por lo tanto, el nuevo valor «residual» puede superar en un 50 % de los casos el límite positivo o negativo. En este caso, el número se convierte en el «opuesto»: cambia de signo y acaba a una distancia M del original.
Es importante entender que esta transformación se produce sólo debido a una interpretación diferente del estado del bit en la representación interna, y el estado en sí es el mismo para los números con y sin signo.
Vamos a explicarlo con un ejemplo para los tipos enteros más pequeños: char y uchar.
Dado que unsigned char puede almacenar valores de 0 a 255, 256 mapea a 0, -1 mapea a 255, 300 mapea a 44, y así sucesivamente. Si intentamos escribir 300 en un char con signo normal, obtendremos también 44, porque 44 está en el rango de 0 a 127 (el rango positivo de char). Sin embargo, si establece las variables char y uchar en 3000, la imagen será diferente. El resto de 3000 dividido entre 256 es 184. Acaba en uchar sin cambios. Sin embargo, para char, la misma combinación de bits da como resultado -72. Es fácil comprobar que 184 y -72 difieren en 256.
En el siguiente ejemplo es fácil detectar el problema gracias al aviso del compilador.
char c = 3000; // truncation of constant value
|
Sin embargo, si obtiene un número extra grande durante el cálculo, no habrá ningún aviso.
char c55 = 55;
|
Puede producirse un efecto similar cuando se utilizan números enteros con y sin signo del mismo tamaño en la misma expresión, ya que el operando con signo se convierte en operando sin signo. Por ejemplo:
uint u = 11;
|
Cuando se suman dos números enteros negativos obtenemos el resultado esperado. La segunda expresión asigna la suma de -38 al número sin signo «opuesto» 4294967258.
No se recomienda mezclar tipos con y sin signo en la misma expresión debido a estos posibles problemas.
Además, si restamos algo de un entero sin signo, tenemos que asegurarnos de que el resultado no sea negativo. De lo contrario, se convertirá en un número positivo y puede distorsionar la idea del algoritmo, en concreto la idea del operador cíclico while que comprueba en la variable la condición «mayor o igual que cero»: como los números sin signo son siempre no negativos, podemos obtener fácilmente un operador cíclico infinito, es decir, que el programa se cuelgue.