Где это встречается?

Большинство программистов в наше время пишут код без понятия, что происходит там "под капотом", банально как хранятся в памяти те или иные типы переменных и у них это не вызывает никаких проблем. А что будет, если оставить только базовые операторы?

Дано: некий измерительный преобразователь отправляет float по протоколу modbus в виде двух регистров word, мы их получаем на ПЛК.
Задача: полученные два слова сконвертировать обратно в float.

Float по стандарту IEE754

Стандарт IEE754 определяет как представлять отрицательные и положительные числа с плавающей точкой. Множество статей в интернете описывает тонкости это стандарта с приложенными математическими формулами и подробным описаниям.

Битовое представление числа с плавающей точкой<br />
Битовое представление числа с плавающей точкой

Самое главное, что нам нужно знать из этого стандарта:

  • float это 32 бита
  • где 1-й бит - знак, следующие 8 - порядок, последние 23 - мантисса
  • зная эти данные и подставив их в формулу можно получить искомое значение

Как обычно это решается?

Обычно это решается быстро и легко через указатели или через встроенные функции конвертации. Что в принципе не требует знаний о стандарте.

Я же за неимением онного использовал битовые операции.

Решение с помощью битовых операций

Зная формат, несложно получить знак, порядок и мантиссу из битового представления числа. Делается это с помощью нескольких битовых масок. Затем полученные числа подставляются в формулу и вычисляется результат.

PROGRAM PLC_PRG
VAR real_dword, sign, mant, expb, bmask1, bmask2, bmask3: DWORD;
word1_input, word2_input: WORD;
float_output, signf, expf: REAL;	
	mantf: REAL;
END_VAR

word1_input := 11047;
word2_input := 16964;
bmask1 := 16#FF;
bmask2 := 16#7FFFFF;
bmask3 := 16#800000;
(* Складываем два слова в одно двойное *)
real_dword := SHL(WORD_TO_DWORD(word2_input), 16) + WORD_TO_DWORD(word1_input);
(* Находим знак *)
sign := SHR(real_dword, 31);
IF sign > dword#0 THEN
    signf := -1.0;
ELSE
    signf := 1.0;
END_IF;
(* Находим порядок *)
expb := SHR(real_dword, 23) AND bmask1;
(* Находим мантиссу *)
IF(expb <> dword#0 ) THEN
    mant := (real_dword AND bmask2)  OR bmask3;
ELSE
    mant := SHL((real_dword AND bmask2), 1);    
END_IF;
(* Подставляем полученные данные в формулу *)
mantf := (DWORD_TO_REAL(mant)) * (EXPT(REAL#2.0, DINT#-23));
expf := expt(real#2.0 , dword_to_dint(expb - dword#127));

float_output := signf *  expf * mantf;

Этот код немного отличается от изначального, который я писал для AXC 1050 в PC Worx, из-за невозможности протестировать его на сегодняшний день нигде кроме эмулятора в CodeSys.

Это несущественно, разница лишь в способе объявления переменных

Чем можно проверить свое решение

Пока гуглил все эти стандарты и способы решений, нашел один интересный Excel документ от Schneider Electric. С его помощью вы можете быстро проверить свое решение.