LabVIEW NXG — простые типы данных и приведение типов

    Это третья статья из цикла о 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Е в старшем байте.


    После трёх статей мы должны достаточно уверенно работать с тремя типами данных:



    Если есть какие-либо вопросы по изложенному материалу — спрашивайте в комментариях, а в следующей статье мы рассмотрим массивы.

    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое