Números enteros

Los tipos Números enteros están pensados para almacenar números sin decimales. Deben elegirse si el sentido práctico del valor excluye las fracciones. Por ejemplo, los números de las barras de un gráfico o de posiciones abiertas son siempre números enteros.

MQL5 permite elegir tipos enteros de 1 a 8 bytes de tamaño utilizando las palabras clave char, short, int y long, respectivamente. Todos ellos son de tipo con signo, es decir, pueden contener tanto valores positivos como negativos. Si es necesario, los tipos enteros que tengan los mismos tamaños pueden declararse sin signo (sus nombres empiezan por «u» de «unsigned»): uchar, ushort, uint y ulong.

En función del tamaño del tipo y de si lleva o no lleva signo, los rangos de valores potenciales son los que se muestran en la siguiente tabla:

Tipo

min

max

char

-128

127

uchar

0

255

short

-32768

32767

ushort

0

65535

int

-2147483648

2147483647

uint

0

4294967295

long

-9223372036854775808

9223372036854775807

ulong

0

18446744073709551615

No es necesario memorizar los valores límite anteriores para cada número entero. Hay muchas constantes con nombre predefinidas en MQL5 que se pueden utilizar en un código en lugar de números «mágicos», incluyendo los números enteros más bajos o más altos. Esta tecnología se aborda en una sección dedicada al preprocesador. Aquí nos limitamos a enumerar las constantes con nombre relevantes: CHAR_MIN, CHAR_MAX, UCHAR_MAX, SHORT_MIN, SHORT_MAX,USHORT_MAX, INT_MIN, INT_MAX, UINT_MAX, LONG_MIN, LONG_MAX y ULONG_MAX.

Vamos a explicar cómo se obtienen estos valores, para lo cual es necesario volver a bits y bytes.

El número de todas las combinaciones posibles de diferentes estados de 8 bits, activados y desactivados, dentro de un byte, es 256. Esto produce el rango de valores 0-255 que puede almacenarse en un byte. Sin embargo, su interpretación depende del tipo para el que se haya asignado este byte. El compilador garantiza diferentes interpretaciones en función de las sentencias del programador.

El bit de orden inferior (más a la derecha) de un byte significa 1, el segundo 2, el tercero 4, y así sucesivamente hasta el bit de orden superior, que significa 128. Es evidente que estos números son iguales a dos elevado a una potencia igual al número de bits (la numeración empieza por 0). Este es el efecto de utilizar el sistema binario.

Bits

orden superior

orden inferior

Número

7

6

5

4

3

2

1

0

Valor

128

64

32

16

 8

 4

 2

 1

Cuando todos los bits están activados, se obtiene la suma de todas las potencias de dos, es decir, 255 es el valor más alto para un byte. Si se reinician todos los bits, obtenemos cero. Si se activa un bit de orden inferior, el número es impar.

En la codificación de números con signo, el bit de orden superior se utiliza para marcar valores negativos. Por lo tanto, para un entero de un solo byte dentro del rango positivo, 127 se convierte en el valor más alto. Para los valores negativos hay 128 combinaciones posibles, es decir, el valor más bajo es -128. Cuando todos los bits de un byte están activados, ello se interpreta como -1. Si en un número de este tipo se reinicia el bit de orden inferior, obtendremos -2, etc. Si sólo se activa el bit de orden superior (signo) y todos los demás bits se reinician, obtendremos -128.

Esta codificación, que puede parecer irracional, se llama «adicional» y le permite unificar los cálculos de números con y sin signo a nivel del hardware. Además, le permite no perder un valor, lo que ocurriría si las regiones positivas y negativas se codificaran de forma idéntica: y es que así habríamos obtenido dos valores para el cero, es decir, un 0 positivo y un 0 negativo. Es más, esto provocaría ambigüedad.

Los números con más bytes, es decir, 2, 4 u 8, tienen una numeración consecutiva similar de bits y la progresión de sus respectivos valores. En todos los casos, un criterio para la negatividad del número es el bit de orden superior activado del byte de orden superior.

Así, podemos utilizar un byte para almacenar un entero sin signo (uchar, es decir, carácter sin signo abreviado) dentro del rango 0-255. También podemos escribir un entero con signo en el byte (para lo cual describiremos su tipo como char). En este caso, el compilador dividirá la cantidad disponible de combinaciones de 256 a partes iguales entre valores positivos y negativos, habiéndola desplegado sobre la región de -128 a 127 (el valor 256 es cero). Es evidente que los valores 0-127 se codificarán por igual a nivel de bits para bytes con y sin signo. Sin embargo, los valores absolutos grandes, a partir de 128, se convertirán en negativos (según el esquema descrito arriba). Esta «transformación» sólo tiene lugar en el momento de la lectura o de la realización de cualquier operación con el valor almacenado, con idéntica representación interna de los datos (estado de los bits).

Examinaremos esta cuestión con más detalle en la sección dedicada a la conversión de tipos.

Del mismo modo que con los enteros de un byte, es fácil calcular que el número de combinaciones de bits es de 65536 para 2 bytes. Por lo tanto, los rangos se forman para el entero de dos bytes con signo y sin signo, short y ushort. Otros tipos permiten almacenar valores aún mayores gracias al aumento de su tamaño en bytes.

Tenga en cuenta que el uso de un tipo sin signo con el mismo tamaño permite duplicar el valor positivo más alto. Esto puede ser necesario para almacenar cantidades potencialmente muy grandes para las que no pueden aparecer valores negativos. Por ejemplo, el número de orden en MetaTrader 5 es un valor del tipo ulong.

Ya hemos visto muestras de la descripción de números enteros en la Parte 1. En concreto, ahí se definió el parámetro de entrada GreetingHour del tipo uint:

input uint GreetingHour = 0;

A excepción de la palabra clave adicional, input, que hace que la variable sea visible en la lista de parámetros de un programa MQL, los demás componentes, es decir, el tipo, el nombre y la inicialización opcional tras el signo '=', son intrínsecos a todas las variables.

La sintaxis de la descripción de variables se estudiará en detalle en el apartado Variables. Por ahora, tenga en cuenta el método de registro de las constantes de tipo entero. Al describir una variable se pueden especificar constantes como valor por defecto (en el ejemplo anterior es 0). Además, las constantes pueden utilizarse en expresiones; por ejemplo, en un evento de fórmula.

Conviene recordar que las constantes de cualquier tipo, insertadas en el código fuente, se denominan literales (textualmente, «palabra por palabra»). Su nombre se debe a que se introducen en el programa «tal cual» y se utilizan inmediatamente en el punto de descripción. Los literales, a diferencia de muchos otros elementos del lenguaje, en particular las variables, no tienen nombre y no se puede hacer referencia a ellos desde otros puntos del programa.

Para los números negativos es obligatorio poner el signo menos '-' delante del número; sin embargo, el signo más '+' puede omitirse para los números positivos, es decir, las formas '+100' y '100' son idénticas.

Hay que tener en cuenta que los valores numéricos suelen registrarse en el código fuente con nuestra notación decimal habitual. No obstante, MQL5 permite utilizar la otra, es decir, la hexadecimal. Ello es conveniente para procesar información a nivel de bits (véase Operaciones a nivel de bits).

En las constantes decimales se permiten los números del 0 al 9 en números con cualquier orden de dígitos, mientras que para las hexadecimales, además de los dígitos, se utilizan también los símbolos latinos de A a F o de a a f (es decir, no afecta que sean mayúsculas o minúsculas). El «dígito hexadecimal» A se corresponde con el número 10 de la notación decimal, B con 11, Ccon 12, etc., y así hasta F, que es igual a 15.

Una característica distintiva de una constante hexadecimal es el hecho de que comienza con el prefijo 0x o 0X, seguido de los órdenes de dígitos significativos del número. Por ejemplo, el número 1 se registra como 0x1 en el sistema hexadecimal, mientras que el 16 como 0x10 (se requiere un dígito de orden superior adicional porque 16 es mayor que 15, es decir, 0xF). El 255 decimal se convierte en 0xFF.

Veamos algunos ejemplos más que ilustran diversas situaciones del uso de tipos enteros en la descripción de variables (adjuntos en el script MQL5/Scripts/MQL5Book/p2/TypeInt.mq5):

void OnStart()
{
  int x = -10;          // ok, signed integer x = -10
  uint y = -1;          // ok, but unsigned integer y = 4294967295
  int z = 1.23;         // warning: truncation of constant value, z = 1
  short h = 0x1000;     // ok, h = 4096 in decimal
  long p = 10000000000// ok
  int w = 10000000000;  // warning, truncation..., w = 1410065408
}

La variable x se inicializa correctamente con el valor negativo permitido, -10.

La variable y no lleva signo. Por lo tanto, si se intenta registrar en él un valor negativo, se produce un efecto interesante. El número -1 tiene una representación en bits, que es interpretada por el programa de acuerdo con el tipo sin signo, uint. Por lo tanto, se obtiene el número 4294967295 (en realidad es igual a UINT_MAX).

La variable z se asigna con el número de punto flotante 1,23 (se analizarán en la siguiente sección) y el compilador advierte sobre el truncamiento de la parte fraccionaria. Como resultado, el entero 1 entra en la variable.

La variable h se inicializa correctamente mediante una constante en forma hexadecimal (0x1000 = 4096).

El valor grande 10000000000 se registra en las variables p y w, la primera de las cuales es de tipo entero largo (long) y se procesa correctamente, mientras que la segunda es de tipo normal (int) y, por tanto, llama la atención del compilador. Dado que la constante supera el valor máximo de int, el compilador trunca el exceso de dígitos de orden superior (bits) y, de hecho, 1410065408 se convierte en w.

Este comportamiento es una de las posibles evoluciones negativas de las conversiones de tipos que el programador puede o no suponer. En este último caso, ello conlleva un error potencial. Es evidente que, en este ejemplo concreto, se han seleccionado valores erróneos de forma intencionada para hacer una demostración de las advertencias. En un programa real no siempre es tan obvio qué valores está intentando guardar el programa en la variable entera. Por lo tanto, debe examinar detenidamente las advertencias del compilador y tratar de eliminarlas, una vez cambiado el tipo o especificado explícitamente la conversión de tipos requerida. Esta cuestión se abordará en la sección dedicada a la conversión de tipos.

Para los tipos enteros se definen operaciones aritméticas, a nivel de bits y de otros tipos (véase el capítulo Expresiones).