Коварные нули

Всё началось в того, что мне захотелось добавить поддержку plutosdr в свой проект r2cloud. Для этого я открыл код, который читает данные из rtlsdr и увидел следующее:

for (int i = 0; i < 0x100; ++i) {
	lookupTable[i] = ((i & 0xff) - 127.4f) * (1.0f / 128.0f);
}

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

Для начала нужно дать небольшой контекст. Все SDR приёмники имеют фиксированную разрядность АЦП. Для rtl-sdr - это 8 бит. Поэтому значения, которые выдаёт приёмник, находятся в интервале от 0 до 255. Однако, DSP блоки работают с float числами, поэтому нужно преобразовать byte во float в интервале от -1 до 1. Чтобы в рантайме такое преобразование работало быстро, можно сделать таблицу поиска. В ней индекс массива соответствует числу float.

На первый взгляд задача достаточно простая. Нужно взять интервал от -1 до 1 и поделить на все возможные значения - 256. В итоге получается на каждое значение байта нужно: 2 / 256 = 0.0078125. Пока всё просто. Теперь я хочу проверить границы чисел. Для этого я умножу минимальное и максимальное значение байта на 0.0078125.

0.0078125 * - 128 = -1.
0.0078125 * 127 = 0.9921875

Из-за нуля, максимальное значение байта - 127. А значит граница в 1.0 недостижима. Вот схема для примера:

-128
-128
0
0
127
127
0.0078125
0.0078125
0.9921875
0.9921875
-1.0
-1.0
Text is not SVG - cannot display

Из неё следуют две вещи:

  1. все значения float будут “сдвинуты” влево на 0.0078125.
  2. все положительные значения становятся ближе к нулю.

Второй момент очень важен. Дело в том, что некоторые алгоритмы используют float для определения вероятности 0 или 1. То есть, чем ближе значение к “1”, тем более вероятна единица. А чем ближе к “-1” - тем более вероятен ноль. Как только мы сдвинули все положительные значения ближе к нулю, мы уменьшили их вероятность. Это не очень хорошо.

Теперь немного становится понятно, почему алгоритм конвертации такой странный и откуда там float. Тем не менее я так и не понял почему именно 127.4. Ведь если нарисовать для него схему, то получается вот что:

-128
-128
0
0
127
127
127.6/128
127.6/128
-127.4/128
-127.4/128
Text is not SVG - cannot display

Почему-то все значения сдвинуты вправо на дополнительные 0.00078125 от их среднего положения. Я так и не смог понять зачем это сделано. В итоге после многочисленных часов раздумий получилось следующее: