Числа в строки и обратно

Как мы знаем, для преобразования чисел в строки и обратно — строк в числа, можно пользоваться оператором явного приведения типов. Например, для типов double и string, это условно выглядит так:

double number = (double)text;
string text = (string)number;

Разумеется, возможно приведение к другим числовым типам: float, long, int и т.д.

Обратите внимание, что вариант приведения к вещественному типу (float) обеспечивает меньшее количество значащих цифр, что в некоторых задачах может считаться преимуществом, поскольку дает более компактное и легко читаемое представление.

Более того, строго говоря, приведение не обязательно, так как даже в отсутствии оператора явного приведения типа компилятор сделает приведение в неявном виде, хотя и выдаст об этом предупреждение (в связи с чем рекомендуется всегда делать приведения типов явными).

В дополнение к этому в MQL5 API имеется несколько полезных функций. После их описания приведен общий пример.

double StringToDouble(string text)

Функция StringToDouble преобразует строку в число типа double.

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

Процесс конвертации подразумевает, что в строке находится последовательность символов, отвечающих правилам записи литералов числовых типов (как вещественных, так и целых). В частности, строка может начинаться со знака '+' или '-', и цифры, а также состоять далее из цифр.

Для случая вещественных чисел допускается наличие одного символа точки '.', отделяющей дробную часть, и опциональной показательной степени в формате: символ 'e' или 'E', за которым следует последовательность цифр для степени (она также может предваряться знаком '+' или '-').

Для целых чисел поддерживается шестнадцатеричная запись, то есть после префикса "0x" могут следовать не только десятичные цифры, но и 'A', 'B', 'C', 'D', 'E', 'F' (в любом регистре).

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

Начальные пустотелые символы (пробелы, табуляции, переводы строк) пропускаются и не влияют на конвертацию. Если после них идут цифры и прочие отвечающие правилам символы, число будет получено корректно.

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

string

double

Результат

"123.45"

123.45

Одна десятичная точка

"\t   123"

123.0

Пробельные символы в начале игнорируются

"-12345"

-12345.0

Число со знаком

"123e-5"

0.00123

Научный формат с показателем степени

"0x12345"

74565.0

Шестнадцатеричная запись

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

string

double

Результат

"x12345"

0.0

Начинается с неразрешенного символа (буквы)

"123x45"

123.0

Буква после 123 прерывает конвертацию

"   12 3"

12.0

Пробел после 12 прерывает конвертацию

"123.4.5"

123.4

Вторая десятичная точка после 123.4 прерывает конвертацию

"1,234.50"

1.0

Запятая после 1 прерывает конвертацию

"-+12345"

0.0

Два знака — "перебор"

string DoubleToString(double number, int digits = 8)

Функция DoubleToString преобразует число в строку с заданной точностью (количеством цифр от −16 до 16).

Она выполняет работу, схожую с приведением числа к (string), но позволяет выбрать с помощью второго параметра точность представления числа в результирующей строке.

Оператор (string), примененный к double, выводит 16 значащих цифр (совокупно в мантиссе и дробной части). Полного эквивалента этому добиться с помощью функции нельзя.

Когда параметр digits больше или равен 0, он означает количество знаков после "запятой". При этом количество знаков до "запятой" определяется самим числом (тем, насколько оно большое), и если суммарное количество знаков в мантиссе и указанное в digits окажется больше 16, то младшие значащие разряды будут содержать "мусор" (из-за особенностей хранения вещественных чисел). Напомним, что 16 знаков — средняя максимальная точность для типа double, то есть установка digits в 16 (максимум) обеспечит точное представление только значениям меньше 10.

Когда параметр digits меньше 0, он задает количество значащих цифр, и число выводится в научном формате с экспонентой. С точки зрения точности (но не формата записи), установка digits=-16 в функции близка по результату к приведению (string).

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

Следует иметь в виду, что в ходе математических вычислений могут возникать ошибки, приводящие к тому, что результат не является валидным числом, хотя и имеет тип double (или float). Например, переменная может содержать результат вычисления квадратного корня из отрицательного числа.
 
Такие значения называются "не числами" (Not a Number, NaN), и отображаются в случае приведения к (string) как краткая подсказка типа ошибки, например, -nan(ind) (ind — не определено), nan(inf) (inf — бесконечность). В случае использования функции DoubleToString, вы получите некое большое число, не соответствующее действительности.
 
Особенно важно, что все последующие вычисления с NaN будут также давать NaN. Для проверки таких значений существует функция MathIsValidNumber.

 

long StringToInteger(string text)

Функция преобразует строку в число типа long. Обратите внимание, что тип результата именно long, а не int (несмотря на название) и не ulong.

Альтернативный способ заключается в приведении типа с помощью оператора (long). Более того, для приведения можно использовать любой другой целочисленный тип на выбор: (int), (uint), (ulong) и т.д.

Правила конвертации аналогичны типу double, но исключают символ точки и показатель степени из числа разрешенных символов.

string IntegerToString(long number, int length = 0, ushort filling = ' ')

Функция IntegerToString преобразует целое число типа long в строку указанной длины. Если представление числа занимает меньше символов, оно дополняется слева символом filling (по умолчанию, пробелом). В противном случае число выводится целиком, без ограничения. Вызов функции с параметрами по умолчанию эквивалентен приведению к (string).

Разумеется, целые типы меньшего размера (например, int, short) будут обработаны функцией без проблем.

Примеры использования всех вышеописанных функций приведены в скрипте ConversionNumbers.mq5.

void OnStart()
{
   const string text = "123.4567890123456789";
   const string message = "-123e-5 buckazoid";
   const double number = 123.4567890123456789;
   const double exponent = 1.234567890123456789e-5;
   
   // приведение типов
   Print((double)text);    // 123.4567890123457
   Print((double)message); // -0.00123
   Print((string)number);  // 123.4567890123457
   Print((string)exponent);// 1.234567890123457e-05
   Print((long)text);      // 123
   Print((long)message);   // -123
   
   // конверсии функциями
   Print(StringToDouble(text)); // 123.4567890123457
   Print(StringToDouble(message)); // -0.00123
   
   // по умолчанию, 8 цифр в дробной части
   Print(DoubleToString(number)); // 123.45678901
   
   // настраиваемая точность
   Print(DoubleToString(number5));  // 123.45679
   Print(DoubleToString(number, -5)); // 1.23457e+02
   Print(DoubleToString(number, -16));// 1.2345678901234568e+02
   Print(DoubleToString(number16)); // 123.4567890123456807
   //                                    2 последних цифры неточные!
   Print(MathSqrt(-1.0));                 // -nan(ind)
   Print(DoubleToString(MathSqrt(-1.0))); // 9223372129088496176.54775808
   
   Print(StringToInteger(text));      // 123
   Print(StringToInteger(message));   // -123
   
   Print(IntegerToString(INT_MAX));         // '2147483647'
   Print(IntegerToString(INT_MAX5));      // '2147483647'
   Print(IntegerToString(INT_MAX16));     // '      2147483647'
   Print(IntegerToString(INT_MAX16, '0'));// '0000002147483647'
}