Итак, я пытаюсь разобраться с числами с фиксированной точкой. Все идет нормально. Единственное, что меня смутило, так это «дробная» часть числа.
Мое понимание чисел с фиксированной точкой состоит в том, что они разбивают двоичное число в соответствии со шкалой (которая в данном случае равна восьми битам). В левой части будет целая часть, а в правой — дробь. XXX.Y
, где XXX
— три байта для целой/целой части, а Y
— один байт для дробной части (пожалуйста, поправьте меня, если я ошибаюсь).
Возьмем следующие макросы:
#define FIX_SCALE 8
#define FIX_FRACTION_MASK ((1 << FIX_SCALE) - 1)
#define FIX_WHOLE_MASK ~FIX_FRACTION_MASK
#define FIX_FROM_FLOAT(X) ((X) * (1 << FIX_SCALE))
#define FIX_TO_FLOAT(X) ((float)(X) / (1 << FIX_SCALE))
#define FIX_TO_INT(X) ((X) >> FIX_SCALE)
#define FIX_FROM_INT(X) ((X) << FIX_SCALE)
#define FIX_FRACTION(X) ((X) & FIX_FRACTION_MASK)
#define FIX_WHOLE(X) ((X) & FIX_WHOLE_MASK)
Рассмотрим следующий пример:
int Fixed = FIX_FROM_FLOAT(2.5f);
Результирующее целочисленное значение равно 640
, 0x280
в шестнадцатеричном формате и 0000 0000 0000 0000 0000 0010 1000 0000
в двоичном формате.
Возьмем первые два байта: 0000 0010 1000 0000
Я понимаю, откуда 0000 0010
, это целая часть (2
). Но чего я не понимаю, так это 1000 0000
, который является дробной частью. Я просто не понимаю, как это связано с числом 5 (которое в двоичном формате равно 0101). Я ожидал что-то вроде 0101 0000
или 0000 0101
-- Очевидно, я неправильно понимаю здесь фундаментальную концепцию.
Если я напишу:
int Fraction = FIX_FRACTION(Fixed);
Я бы получил 128
(0x80
в шестнадцатеричном формате. Имеет смысл, потому что он маскирует целую часть, которая равна 2
). В первый раз я написал, что рассчитывал получить 5
обратно.
Я получаю 0.5
, если напишу:
float Fraction = FIX_TO_FLOAT(FIX_FRACTION(Fixed));
Может ли кто-нибудь прояснить эту путаницу для меня? Почему в дроби номер 0000 1000
не было 101
? Почему мы должны были сделать FIX_TO_FLOAT
на FIX_FRACTION
, чтобы получить правильную дробь?
Спасибо.