Conversión de tipos

Transformación de tipos numéricos

Muy a menudo surge la necesidad de transformar un tipo de datos en otro. No cada uno de los tipos numéricos puede ser transformado en otro. Las transformaciones admisibles en MQL5 se indican en el esquema:

Esquema de transformaciones permitidas

Las líneas contínuas con flechas indican las transformaciones que se efectuan sin pérdida de la información. El tipo bool puede figurar en vez del tipo char (ambos ocupan 1 byte de la memoria), en vez del tipo int se puede utilizar el tipo color (4 bytes cada uno), y en vez del tipo long se admite el tipo datetime (ocupan 8 bytes cada uno). Cuatro líneas rayadas con flechas de color gris significan las transformaciones en las que puede ocurrirse la pérdida de precisiones. Por ejemplo, la cantidad de dígitos en el número entero 123456789 (int) supera la cantidad de dígitos que puede ser representada en el tipo float.

   int n=123456789;
   float f=n;    // contenido f es igual a 1.234567892E8
   Print("n = ",n,"   f = ",f);
   // resultado n= 123456789    f= 123456792.00000

El número transformado en el tipo float tiene el mismo orden pero con una precisión algo menor. Las transformaciones inversas a las flechas negras se realizan con una posible pérdida de la información. Las transformaciones entre char y uchar, short y ushort, int y uint, long y ulong (se tienen en cuenta las transformaciones directas e inversas) pueden llevar a la pérdida de la información.

Resulta que al transformar los valores con punto flotante en el tipo entero, la parte fraccionaria siempre se omite. Si se necesita redondear el número con punto flotante hasta un número entero más próximo (lo que en mayoría de los casos puede ser más útil) es necesario utilizar la función MathRound().

Ejemplo:

//--- aceleración de la gravedad
   double g=9.8;
   double round_g=(int)g;
   double math_round_g=MathRound(g);
   Print("round_g = ",round_g);
   Print("math_round_g = ",math_round_g);
/*
   Resultado:
   round_g = 9
   math_round_g = 10
*/

Si dos valores se unen con un operador binario, entonces antes de iniciar la operación el operando del tipo menor se transforma en el tipo mayor según la prioridad indicada en la esquema:

Conversión durante la unión de una operación binaria

En las operaciones los tipos de datos char, uchar, short y ushort se convierten al tipo int incondicionalmente.

Ejemplos:

   char   c1=3;
//--- primer ejemplo
   double d2=c1/2+0.3;
   Print("c1/2 + 0.3 = ",d2);
// Resultado:   c1/2+0.3 = 1.3
 
//--- segundo ejemplo
   d2=c1/2.0+0.3;
   Print("c1/2.0 + 0.3 = ",d2);
// Resultado:   c1/2.0+0.3 = 1.8

La expresión calculada se compone de dos partes. En el primer ejemplo la variable c1 del tipo char se transforma en la variable temporal del tipo int, puesto que el segundo operando en la operación de división, la constante 2, tiene el tipo int mayor. Como resultado de la división de números enteros 3/2 obtenemos el valor 1 que tiene el tipo int.

En la segunda operación del primer ejemplo el segundo operando es la constante 0.3 que tiene el tipo double, por eso el resultado de la primera operación se transforma en la variable temporal del tipo double con el valor 1.0.

En el segundo ejemplo la variable c1 del tipo char se transforma en la variable temporal del tipo double, puesto que el segundo operando en la operación de división, la constante 2.0, tiene el tipo double. Las siguientes transformaciones no se realizan.

 

La conversión de tipos numéricos

En las expresiones del lenguaje MQL5 se puede utilizar tanto la conversión explícita de tipos, como la implícita. La conversión explícita de tipos se escribe de la siguiente manera:

var_1 = (tipo)var_2;

En calidad de la variable var_2 puede ser una expresión o resultado de ejecución de la función. Se admite también la inscipción funcional de la conversión explícita de tipos:

var_1 = tipo(var_2);

Vamos a analizar la conversión explícita a base del primer ejemplo.

//--- tercer ejemplo
   double d2=(double)c1/2+0.3;
   Print("(double)c1/2 + 0.3 = ",d2);
// Resultado:   (double)c1/2+0.3 = 1.80000000

Antes de realizar la operación de división la variable c1 se convierte al tipo double. Ahora la constante de número entero 2 se convierte al valor 2.0 del tipo double, puesto que en el resultado de la transformación el primer operando ha obtenido el tipo double. Prácticamente la conversión explícita es una operación unaria.

Además, al intentar realizar la conversión de tipos, el resultado puede salir de los márgenes del rango permitido. En este caso habrá el acortamiento. Por ejemplo:

   char c;
   uchar u;
   c=400;
   u=400;
   Print("c = ",c); // resultado c = -112
   Print("u = ",u); // resultado u = 144

Antes de ejecutar las operaciones (salvo la de asignación), ocurre la transformación en el tipo que tenga la mayor prioridad, y antes de las operaciones de asignación — en el tipo objetivo.

Ejemplos:

   int    i=1/2;        // no hay conversión de tipos, resultado: 0
   Print("i = 1/2  ",i);
 
   int k=1/2.0;         // la expresión se convierte al tipo double,
   Print("k=1/2  ",k);  // luego se convierte al tipo objetivo int, resultado: 0
 
   double d=1.0/2.0;    // no hay conversión de tipos, resultado: 0.5
   Print("d=1/2.0; ",d);
 
   double e=1/2.0;      // la expresión se convierte al tipo double, 
   Print("e=1/2.0; ",e);// que coincide con el tipo objetivo, resultado: 0.5
 
   double x=1/2;        // la expresión del tipo int se convierte al tipo objetivo double,
   Print("x=1/2; ",x);  // resultado: 0.0

Durante la conversión del tipo long/ulong al tipo double puede que se pierda la precisión: si el valor entero es más de 9223372036854774784 o menos de -9223372036854774784.

void OnStart()
  {
   long l_max=LONG_MAX;
   long l_min=LONG_MIN+1;
//--- buscamos el valor máximo entero que no pierde la precisión cuando se convierte al tipo double
   while(l_max!=long((double)l_max))
      l_max--;
//--- buscamos el valor mínimo entero que no pierde la precisión cuando se convierte al tipo double
   while(l_min!=long((double)l_min))
      l_min++;
//--- ahora derivamos el intervalo encontrado para los números enteros  
   PrintFormat("Si un número entero se convierte a double, tiene que "
               "encontrarse en el intervalo [%I64d, %I64d]",l_min,l_max);
//--- ahora vamos a ver qué es lo que pasa si el número se queda fuera de este intervalo
   PrintFormat("l_max+1=%I64d, double(l_max+1)=%.f, ulong(double(l_max+1))=%I64d",
               l_max+1,double(l_max+1),long(double(l_max+1)));
   PrintFormat("l_min-1=%I64d, double(l_min-1)=%.f, ulong(double(l_min-1))=%I64d",
               l_min-1,double(l_min-1),long(double(l_min-1)));
//--- el resultado será el siguiente
// Cuando un número entero se convierte a double, este número tiene que encontrarse dentro del intervalo [-9223372036854774784, 9223372036854774784]
// l_max+1=9223372036854774785, double(l_max+1)=9223372036854774800, ulong(double(l_max+1))=9223372036854774784
// l_min-1=-9223372036854774785, double(l_min-1)=-9223372036854774800, ulong(double(l_min-1))=-9223372036854774784
  }

 
Las conversiones para el tipo string

El tipo string tiene la mayor prioridad entre los tipos simples. Por esta razón, si en la operación uno de los operandos tiene el tipo string, entonces el otro operando va a ser convertido al tipo string de una manera automática. Hay que tener en cuenta que para el tipo string se permite sólo operación binaria de suma. Está permitida la conversión explícita de la variable del tipo string a cualquier tipo numérico.

Ejemplos:

   string s1=1.0/8;            // la expresión se convierte al tipo double, 
   Print("s1=1.0/8; ",s1);     // luego al tipo objetivo string, 
// resultado:"0.12500000"(cadena de 10 símbolos)
 
   string s2=NULL;             // deinicialización de la cadena
   Print("s2=NULL; ",s2);      // resultado: cadena vacía
   string s3="Ticket N"+12345; // la expresión se convierte al tipo string 
   Print("s3=\"Ticket N\"+12345",s3);
 
   string str1="true";
   string str2="0,255,0";
   string str3="2009.06.01";
   string str4="1.2345e2";
   Print(bool(str1));
   Print(color(str2));
   Print(datetime(str3));
   Print(double(str4));

 

La conversión de tipos de punteros de las clases bases a los punteros de las clases derivadas

Los objetos de la clase derivada abiertamente también pueden considerarse como los objetos de la clase base que le corresponde. Esto lleva a algunos efectos interesantes. Por ejemplo, a pesar del hecho que los objetos de diferentes clases derivadas de una clase base, puedan diferenciarse de una manera significativa uno del otro, podemos crear su lista asociada (List) puesto que los consideramos como los objetos de la clase base. Pero lo invertido no es correcto: resulta que los objetos de la clase base no son los objetos de la clase derivada de una forma automática.

Se puede utilizar la conversión explícita de tipos para la transformación de los punteros de la clase base en los punteros de la clase derivada. Pero hay que estar completamente seguro de la admisibilidad de esta transformación, porque en el caso contrario surgirá un error crítico de tiempo de ejecución y el programa-mql5 se detendrá.

Conversión dinámica de tipos  con ayuda del operador dynamic_cast #

Existe la posibilidad de realizar la conversión dinámica de tipos con la ayuda del operador dynamic_cast, que puede ser aplicado solo a los punteros de clase. En este caso, la comprobación de la corrección de los tipos se realiza en el momento de ejecución del programa. Esto significa que al usar el operador dynamic_cast, el compilador no realiza la comprobación del tipo de datos usado para la conversión. En caso de que se realice la transformación de un puntero en un tipo de datos que no sea un tipo concreto de objeto, el resultado  será  el valor NULL.

dynamic_cast <type-id> ( expression )

El parámetro type-id entre corchetes angulares debe ser el puntero al tipo de clase anteriormente determinado. El tipo de operando expression (a diferencia de C++) puede ser cualquiera, excepto void.

Ejemplo:

class CBar { };
class CFoo : public CBar { };
 
void OnStart()
  {
   CBar bar;    
//--- la conversión dinámica del tipo de puntero *bar al puntero *foo está permitida
   CFoo *foo = dynamic_cast<CFoo *>(&bar); // no aparecerá un error crítico de ejecución   
   Print(foo);                             // foo=NULL      
//--- el intento de conversión explícita del enlace de un objeto del tipo Bar a un objeto del tipo Foo no está permitido
   foo=(CFoo *)&bar;                       // aparecerá un error crítico de ejecución
   Print(foo);                             // esta línea no se ejecutará
  }

Véase también

Tipos de datos