Целые числа

Целочисленные типы предназначены для хранения чисел без дробной части. Их следует выбирать, когда прикладной смысл значения исключает дроби. Например, количество баров на графике или количество открытых позиций всегда равно целому числу.

MQL5 позволяет выбрать целые типы с размером от 1 до 4 байт с помощью ключевых слов char, short, int, long соответственно. Все они являются знаковыми, то есть могут содержать как положительные, так и отрицательные значения. При необходимости целые типы, при тех же размерах, могут объявляться беззнаковыми (их названия начинаются с буквы 'u', unsigned): uchar, ushort, uint, ulong.

В зависимости от размера и "знаковости" типа, диапазоны возможных значений приведены в следующей таблице.

Тип

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

Запоминать вышеприведенные пограничные значения для каждого целочисленного типа нет необходимости. В MQL5 существует много предопределенных именованных констант, которые можно использовать в коде вместо "магических" чисел, включая и минимальные/максимальные целые. Данная технология рассматривается в разделе, посвященном препроцессору. А здесь мы лишь перечислим соответствующие именованные константы: CHAR_MIN, CHAR_MAX, UCHAR_MAX, SHORT_MIN, SHORT_MAX,USHORT_MAX, INT_MIN, INT_MAX, UINT_MAX, LONG_MIN, LONG_MAX, ULONG_MAX.

Поясним, каким образом получаются эти значения. Для этого потребуется вернуться к битам и байтам.

Количество возможных сочетаний различных состояний 8 битов (включенных и выключенных) внутри одного байта равно 256. Это дает диапазон значений от 0 до 255, которые может хранить байт. Но их интерпретация зависит от типа, под который выделен данный байт. Разную интерпретацию обеспечивает компилятор, согласно инструкциям программиста.

Младший бит в байте (самый правый) обозначает число 1, второй бит — 2, третий — 4, и так далее вплоть до старшего бита, который соответствует 128. Не трудно заметить, что эти числа равны двойке, возведенной в степень, равную номеру бита (нумерация идет с 0). Это следствие использования двоичной системы.

Биты

старшие

младшие

Номер

7

6

5

4

3

2

1

0

Значение

128

64

32

16

 8

 4

 2

 1

Когда все биты взведены, это дает сумму всех степеней двойки, то есть 255 — максимальное значение для байта. Если все биты сброшены, получаем ноль. Когда включен младший бит, число нечетное.

При кодировании знаковых чисел старший бит используется для пометки отрицательных значений. Поэтому для однобайтового целого в положительном диапазоне максимальным значением становится 127. Для отрицательного диапазона остается 128 возможных сочетаний, т.е. минимальное значение равно -128. Когда все биты в байте взведены, это интерпретируется как -1. Если у такого числа сбросить младший бит, получим -2, и так далее. Когда взведен только старший бит (знак), а все остальные сброшены — получаем -128.

Такое, на первый взгляд, нелогичное кодирование называется "дополнительным". Оно позволяет унифицировать вычисления знаковых и беззнаковых чисел на аппаратном уровне. Кроме того, оно позволяет не терять одно значение, что произошло бы, если бы положительная и отрицательная области кодировались одинаково: тогда у нас оказалось бы два значения для нуля — положительный 0 и отрицательный 0, что кроме прочего вносило бы двусмысленность.

Числа с бОльшим количеством байтов (2, 4, 8) имеют аналогичную сквозную нумерацию битов и прогрессию соответствующих им значений. Во всех случаях признаком отрицательности числа является взведенный старший бит старшего байта.

Итак, мы можем использовать байт для хранения целого числа без знака (uchar, сокращение от unsigned character) в диапазоне 0-255. А можем записать в байт целое число со знаком (для чего опишем его тип как char), и в этом случае компилятор поделит доступное количество сочетаний 256 поровну между положительными и отрицательными значениями, отобразив его на область от -128 до 127 (256-е значение — это ноль). Как нетрудно догадаться, значения от 0 до 127 будут кодироваться на уровне битов одинаково для знакового и беззнакового байтов. Однако бОльшие абсолютные значения, начиная со 128, "превратятся" в отрицательные (по схеме, описанной во врезке выше). Данное "превращение" происходит только в момент чтения или выполнения операций над хранимым значением, при одинаковом внутреннем представлении данных (состоянии битов).

Мы изучим этот вопрос подробнее в разделе, посвященном приведению типов.

По аналогии с однобайтовыми целыми числами, легко вычислить, что количество сочетаний битов для 2 байт составляет 65536. Отсюда формируются диапазоны для знакового и беззнакового двухбайтового целого (short и ushort). Остальные типы позволяют хранить еще большие значения за счет увеличения размера в байтах.

Обратите внимание, что использование беззнакового типа при том же размере позволяет увеличить в два раза максимальное положительное значение. Это бывает необходимо для хранения потенциально очень больших величин, для которых исключено появление отрицательных значений. Например, номер приказа в MetaTrader 5 является значением типа ulong.

Примеры описания целочисленных переменных мы уже встречали в Части 1. Там, в частности, был определен входной параметр GreetingHour типа uint:

input uint GreetingHour = 0;

За исключением дополнительного ключевого слова input, которое делает переменную видимой в списке параметров MQL-программы, остальные компоненты — тип, имя, и опциональная инициализация после знака '=' — свойственны всем переменным.

Про синтаксис описания переменных будет подробно рассказано в разделе Переменные. Пока же обратим внимание на способ записи константных значений целого типа. При описании переменной они могут быть указаны в качестве значения по умолчанию (в примере выше это 0). Кроме того, константы могут использоваться в выражениях, например, при вычислении по формуле.

Напомним, что константы любого типа, вставленные в исходный код, называются литералами (дословно, "буквальными"). Их название происходит от того факта, что они внедряются в программу "как есть" и используются непосредственно по месту описания. Литералы, в отличие от многих других элементов языка, в частности, переменных, не имеют имен и на них нельзя сослаться из других мест программы.

Для отрицательных чисел указание знака минус '-' перед числом обязательно, но для положительных чисел знак плюс '+' можно опускать, то есть форма +100 и просто 100 — идентичны.

Стоит отметить, что числовые значения обычно записываются в исходном коде в привычной нам десятичной системе счисления. Однако MQL5 допускает использование и другой — шестнадцатеричной. Она удобна при обработке информации на уровне битов (см. Побитовые операции).

Если в константах десятичной системы допустимы цифры от 0 до 9 во всех разрядах, то для шестнадцатеричных — кроме цифр дополнительно используются латинские символы от A до F или от a до f (то есть регистр не имеет значения). "Шестнадцатеричная цифра" A соответствует числу 10 десятичной системы, B — 11, С — 12 и так далее вплоть до F, равного 15.  

Отличительной чертой шестнадцатеричной константы является то, что она начинается с префикса 0x или 0X, за которым следуют значащие разряды числа. Например, число 1 в шестнадцатеричной системе записывается 0x1, а 16 — 0x10 (требуется дополнительный старший разряд, потому что 16 больше 15, то есть 0xF). Десятичное 255 превращается в 0xFF.

Приведем еще несколько примеров, иллюстрирующих различные ситуации с использованием целочисленных типов при описании переменных (прилагаются в скрипте 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
}

Переменная x правильно инициализируется допустимым отрицательным значением -10.

Переменная y является беззнаковой, и потому попытка записать в неё отрицательное значение приводит к интересному эффекту. Число -1 имеет представление в битах, которое программа интерпретирует в соответствии с беззнаковым типом uint и потому получается число 4294967295 (оно, кстати, равно UINT_MAX).

Переменной z присваивается вещественное число 1.23 (они будут рассмотрены в следующем разделе), и компилятор выдает предупреждение об отбрасывании дробной части. В результате в переменную попадает целое значение 1.

Переменная h успешно инициализируется константой в шестнадцатеричной форме (0x1000 = 4096).

Большое значение 10000000000 записывается в переменные p и w, первая из которых длинного целого типа (long) и обрабатывается успешно, в вторая — обычного (int), и потому вызывает предупреждение компилятора. Поскольку константа превышает максимальное значение для int, компилятор отсекает лишние старшие разряды (биты) и фактически в w попадает величина 1410065408.

Такое поведение является одним из возможных негативных проявлений преобразований типов, которые могут подразумеваться программистом, а могут — и нет. В последнем случае это чревато потенциальной ошибкой. Разумеется, в данном конкретном примере некорректные значения были выбраны намеренно для демонстрации предупреждений. В реальной программе не всегда столь очевидно, какое значение программа попытается сохранить в целочисленную переменную, и потому предупреждения компилятора стоит изучить очень внимательно, и постараться от них избавиться, поменяв тип или явно указав требуемое приведение типов. Об этом будет рассказано в разделе о приведении типов.

Для целых типов определены арифметические, побитовые и прочие виды операций (см. главу Выражения).