Опять в школу или урок компьютерной грамотности.

May 06, 2012 13:08


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

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

Задачником выступал этот самый документик, ответами к задачкам - примеры расшифровки данных.

Собственно задачка: есть 4 байта. Это 32 битное число с плавающей точкой (float). Порядок байтов - little endian. Необходимо получить это самое вещественное число.

Проблема заключается в том, что в erlang'е нет float, есть double (это 64 битное число с плавающей точкой). Первая же идея - преобразовать 32 битное бинарное представление в 64 битное и его уже преобразовать. Благо работать с бинарными данными на erlang'е одно удовольствие.

Чтобы получить это число, надо понять как компьютер хранит вещественные числа в памяти.
Описание форматов есть в википедии, общая идея такая:
1) Как всегда, один бит отводится на знак.
2) Далее следует так называемая экспонента - это максимальное из чисел, в которые можно возвести 2, чтобы не превзойти по абсолютной величине искомое число. В случае float отводится 8 бит, в случае double - 11.
3) Остальные биты - мантисса. После некоего преобразования этой мантиссы, мы получим число, умножив на которое 2^e мы получим искомое. Float - 23 бита, double - 52.

С экспонентой, на самом деле, есть небольшая хитрость - в случае float от этого числа надо отнять 127, в случае с double - 1023. В подробности я не вдавался.
Алгоритм преобразования float в double получается достаточно простым:
1) Бит знака остается без изменений
2) Экспонента увеличивается на 892 (1023 - 127)
3) К мантиссе справа добавляется необходимое количество нулевых битов.

Как это будет выглядеть на erlang'е:

binary_to_float(<>) ->
NewExponent = Exponent + 892,
<> = <>,
F.
Ну а далее я начал проверять на тех числах, которые были в документации. Там было даже два набора данных.
Исходные бинарные данные: 0x2F1A2342. Закодированное число: 58,0280029296875
И 0x714DB545: 56,3006184895833

Полученные мной числа:
40.775569915771484
5801.68017578125.

Написал функцию расшифровки, без преобразования в промежуточное 64-битное.

binary_to_float1(<
math:pow(-1, Sign) * math:pow(2, Exponent - 127 - 23) * ((1 bsl 23) + Mantisse).
Тоже не получается.
Прогнал на обеих функциях примеры из википедии. Все сходится.

Значит ответы в задачнике не верны.

Для очистки совести проверил в Си:

#include

int main() {
unsigned long i = 0x42231A2F;
float *f = (float *) &i;
printf("%f\n", *f);
i = 0x45B54D71;
printf("%f\n", *f);
return 0;
}
Вывод программы:
40.775570
5801.680176

ЧТД.

Домашнее задание:
0. Как назвать человека, который писал документацию?
1. Как из мантиссы получить искомый множитель?
2. Какая из написанных на erlang'е двух функций преобразования 32-битной бинарной строки в double будет выполняться быстрее и почему?
3. Напишите функцию преобразования 64-битной бинарной строки на любом языке программирования.
4. Почему отличается числа, полученные в erlang'е и в Си?
5*. Предложите алгоритм перемножения двух float. Разрешено оперировать только целыми числами.
6*. Предложите алгоритм сложения двух float. Опять же, операции только с целыми числами.

Всем кто досидел до конца урока и не уснул - зачет автоматом.
Тем кто уснул - на следующее занятие приходить с родителями.
Тем кто сделал ДЗ - дополнительные баллы на экзамене по количеству решенных задач.
Кто решил хотя бы одну задачу со звездочкой - 5 на экзамене автоматом.

erlang, work, mindfuck

Previous post Next post
Up