Это третья статья из цикла о LabVIEW NXG, в которой мы рассмотрим простые типы данных и несложные преобразования между ними.
Система типов LabVIEW весьма похожа на "классические" языки программирования. В данной статье мы рассмотрим базовые типы — численные, булевские и строки (хотя формально строки не относятся к простым скалярным типам).
Самый простой способ понять систему типов — сделать небольшой инструмент с контролами и индикаторами разных типов:
и соединим их на диаграмме вот так:
Чтобы подчеркнуть различие контролов и индикаторов, я добавил к именам " In" и " Out". Это общепринятая практика в LabVIEW, так как подчёркивает принцип потоков данных, когда контролы и индикаторы используются как терминалы подприборов (о которых мы ещё поговорим).
В случае с численными индикаторами мы можем один из четырнадцати доступных нам типов — знаковые и беззнаковые целые, занимающие 8, 16, 32 или 64 бита, числа с плавающей точкой одинарной или двойной точности (они будут занимать 4 или 8 байт), комплексные числа также одинарной или двойной точности и числа с фиксированной точностью — также простые или комплексные. Тип переключается вот здесь в контекстном меню:
Или вот здесь в панели свойств:
При первом присоединении числового индикатора к проводнику он автоматически меняет тип на тип проводника (а тип проводника совпадает с типом контрола, к которому он подключён). Что произойдёт, если тип индикатора будет отличаться от типа проводника? Вот смотрите:
У нас "загорелись" красные точки (которые называются Coercion Dots), которые говорят нам о том, что в местах подсоединения происходит неявное преобразование типа.
В случае если мы преобразуем SGL в DBL или, скажем, U8 в I32, то ничего страшного не произойдёт, однако преобразование в обратном направлении DBL->SGL, либо DBL->I32 или I32->U8 может привести к потере точности, либо округлениям:
Хорошим "тоном" программирования на LabVIEW считается полное отсутствие Coercion Dots на диаграмме, тем более что каждая такая точка будет добавлять вам предупреждение:
Как поступить в том случае, если вам действительно нужны явные преобразования типов? Для этого есть соответствующие примитивы преобразования, которые находятся вот здесь в палитре Data Types->Numeric->Conversion:
А в контекстном меню эти функции находятся вот здесь:
Вставив соответствующий элемент в проводник вы явно укажете как вы хотите преобразовать данные и это уберёт Coercion Dot, равно как и предупреждение.
Неявное преобразование типов также распространяется и на математические примитивы, например, если вы хотите прибавить целое к числу с плавающей точкой, то вначале целое будет приведено к типу DBL, и лишь затем будет выполнено сложение:
Кстати, вы можете использовать несколько терминалов при использовании некоторых арифметических операций, для этого надо просто потянуть вниз или вверх вот здесь (в классической LabVIEW приходилось менять этот примитив на Compound Arithmetic):
По сравнению с классическими текстовыми языками у LabVIEW есть некоторые отличия. Допустим, вы хотите поделить два целых:
double res;
res = 5/3;
Console.WriteLine(res);
результат будет "1".
А в LabVIEW будет выполнено приведение типов, и результат будет 1,66 (1,66666666666667 если быть дотошным):
Следует отметить, что LabVIEW предоставляет вам немножко больше свободы, чем, скажем, C# (степень "типизированности" у всех языков разная).
Вот, к примеру, мы хотим сложить два байта:
Как видите, здесь не возникает никаких предупреждений и никакого приведения. Самом собой, мы получим переполнение, но мы получаем именно то, что хотим:
В то время как в C# такое даже компилятор не пропустит (ибо операция сложения для байтов не определена, они будут неявно перобразованы в int, затем сложены, а неявное преобразование обратно в байт невозможно:
static void Main(string[] args)
{
byte x_U8 = 200, y_U8 = 200, res_U8;
res_U8 = x_U8 + y_U8;
Console.WriteLine(res_U8);
}
И вам придётся сделать вот так:
res_U8 = (byte)(x_U8 + y_U8);
Обратите внимание, что подобное приведение можно сделать и в LabVIEW, явно указав тип терминалов операции сложения, при этом LabVIEW укажет нам, на то, что мы вручную изменили тип синей точкой на выходе (при этом предупреждения не будет):
Но лучше всего при подобных перобразованиях делать вот так, причём можно использовать комментарии на проводниках, чтобы подчеркнуть тип данных:
Ещё одна особенность LabVIEW (которая присутствует и во многих "традиционных" языках) — наличие бесконечности и неопределённости.
Прежде чем продолжить с LabVIEW мне хотелось бы расчехлить C# и бросить камушек в огород компании Microsoft, ибо вот такой несложный код:
class Program
{
static void Main(string[] args)
{
Console.WriteLine(5.0/0.0);
}
}
выдаёт мне "8". Но, разумеется, это не "восемь", это "Infinity", просто надо голову повернуть набок. Ирония в том, что это .NET Core — продукт компании Микрософт, запускаемый в консоли Микрософт и под управлением операционной системы Микрософт (и да, я только что поставил новый, "с иголочки" Windows 10 версии 2004). В принципе исходники открыты, и, вероятно, убив некоторое количество времени, я могу разобраться в чём там дело, но если вы считаете, что это нормально, бросьте этот камушек в меня обратно.
Ну, как бы то ни было, давайте поделим на нуль в LabVIEW:
Результат деления на нуль — бесконечность. Минус нуль у нас тоже есть и получится минус бесконечность:
Ну а если делить нуль на нуль, то будет NaN (Not a Number):
Мы можем проанализировать если мы получаем NaN при помощи примитива Not a Number? из палитры сравнения:
Результат этого кода — истина.
Чуть сложнее с целыми.
В С# данный код компилятор не пропустит:
static void Main(string[] args)
{
Console.WriteLine(5/0);
}
но я могу схитрить...
static void Main(string[] args)
{
int zero = 0;
Console.WriteLine(5/zero);
}
… и получирть исключение System.DivideByZeroException, однако в LabVIEW вы этого не дождётесь — здесь вы получите нуль, так что будьте аккуратны.
Преобразования между числами и строками
Напрямую (неявным) образом выполнить такое преобразование нельзя:
Для этого в палитре Conversions предусмотрены соответствующие функции:
Применяется вот так:
В обратную сторону преобразование выполняется аналогичным образом, с той лишь разницей, что функции преобразования находятся в палитре работы со строками:
Применяется вот так:
Ещё один маленький и, возможно, неочевидный нюанс со строками для тех кто привык делать вот так:
static void Main(string[] args)
{
string hello = "Hello, ";
string habr = "Habr!";
Console.WriteLine(hello + habr);
}
В LabVIEW "один в один" не получится, для этого есть отдельная функция объединения строк:
Базовые операции с булевским типом
Ровно тоже самое и с булевским типом. Вы не можете соединить численный проводник с булевским терминалом напрямую и наоборот, но вы можете использовать функции сравнения и примитив Boolean to Integer для обратного преобразования:
Особенности сравнения чисел с плавающей точкой справедливы и для LabVIEW. Данный код вернёт False:
Машинный эпсилон находится в разделе констант, вот здесь:
Type Cast
Ну и напоследок хотелось бы затронуть тему преобразования "Type Cast", которая часто вводит в заблуждение. Я имею ввиду вот эту функцию:
У неё есть дополнительный терминал "Type", который задаёт выходной тип. Ключевой момент здесь в словах "by flattening it and unflattening". Самый ближайший аналог "сериализация/десериализация".
Предположим, для простоты я возьму четырёхбайтный тип SGL и преобразую его в I32:
Чему будет равен output? Правильный ответ "1040187392". Почему такое странное число?
Давайте переключим индикатор в шестнадцатиричное представление и всё станет понятнее:
Это 0x3E000000. Здесь происходит следующее — четыре байта числа с плавающей точкой реинтерпретируются как четырёхбайтное целое. Я намеренно взял число 0,125, потому что если вы посмотрите на представление 0,125 согласно IEEE754, то увидите, что там всего пять битов мантиссы установлено — это и даёт нам значение 3Е в старшем байте.
После трёх статей мы должны достаточно уверенно работать с тремя типами данных:
Если есть какие-либо вопросы по изложенному материалу — спрашивайте в комментариях, а в следующей статье мы рассмотрим массивы.