Перейти к содержанию
    

BlackFin и fract16

Доброго времени суток!

 

Только начал заниматься процессорами BlackFin и обработкой сигналов, возник вопрос такой.

 

Используя тип fract16 пишу код:

 

fract16 data;

 

data = float_to_fract16(0.0);

printf("data = %f, data = 0x%x\n", fract16_to_float(data), (unsigned short)data);

 

Получаю на выходе

data = -0.000005, data = 0x0

 

т.е. число 0 при расшифровке становиться -0.000005.

 

Самое интересное, что когда считаю дисперсию сигнала она тоже может получиться отрицательной.

 

Никто не сталкивался с подобным? Как можно обойти данную проблему? Поможет ли изменение _MAX_FLOAT_RANGE для преобразования в fract16 и обратно?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Доброго времени суток!

 

Только начал заниматься процессорами BlackFin и обработкой сигналов, возник вопрос такой.

 

Используя тип fract16 пишу код:

 

fract16 data;

 

data = float_to_fract16(0.0);

printf("data = %f, data = 0x%x\n", fract16_to_float(data), (unsigned short)data);

 

Получаю на выходе

data = -0.000005, data = 0x0

 

т.е. число 0 при расшифровке становиться -0.000005.

 

Самое интересное, что когда считаю дисперсию сигнала она тоже может получиться отрицательной.

 

Никто не сталкивался с подобным? Как можно обойти данную проблему? Поможет ли изменение _MAX_FLOAT_RANGE для преобразования в fract16 и обратно?

 

означает, что fract16_to_float имеет ошибку преобразования 0 во float -0.000005 (верю Вам на слово и не проверяю)

Что меньше, вообще говоря, стандартного отклонения ошибки при представлении чисел с помощью fract16 (там что-то порядка 2 на 10**-5). В принципе ничего страшного, хотя неприятно, главное, что хоть 0+0+0+0 ... = 0 пускай себе fract16_to_float(0) + fract16_to_float(0)+fract16_to_float(0)... стремится к бесконечности B)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

означает, что fract16_to_float имеет ошибку преобразования 0 во float -0.000005 (верю Вам на слово и не проверяю)

Что меньше, вообще говоря, стандартного отклонения ошибки при представлении чисел с помощью fract16 (там что-то порядка 2 на 10**-5). В принципе ничего страшного, хотя неприятно, главное, что хоть 0+0+0+0 ... = 0 пускай себе fract16_to_float(0) + fract16_to_float(0)+fract16_to_float(0)... стремится к бесконечности B)

 

Спасибо. Понятно, только получается что 0+0+0... = 0, но после преобразования в float получаю отрицательное число, и что мне с ним дальше делать если нужно посчитать сигму (т.е. корень квадратный из него)? И еще тогда вопрос, если есь ошибка преобразования fract16 -> float, то почему нет float -> fract16. Может необходимо изменить диапазон значений преобразуемых величин?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поможет ли изменение _MAX_FLOAT_RANGE для преобразования в fract16 и обратно?

Категорически не стоит менять что-либо в системных заголовочных файлах. На 99.9(9)% это не решит проблему, а только добавит новые.

...и что мне с ним дальше делать если нужно посчитать сигму (т.е. корень квадратный из него)?

А зачем преобразовывать во float? Берите корень из fract.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

число 0 при расшифровке становиться -0.000005.

Однажды я столкнулся с подобным при тестировании FastMath - симулятора

арифметики с плавающей точкой для C62xx/C64xx. Все работало неплохо,

вот только 2.0 * 0.0 = 2.0. Такой вот специфичный баг. Оказалось, что

программист слегка промахнулся при вычислении экспоненты произведения.

 

В вашем случае что-то похожее, только программист, по-видимому,

перестарался с обработкой ошибки округления в преобразовании,

а её тут вообще нет, любое Q16.xx _точно_ содержится в 32-битном

float. Напишите в Аналог, что у них завелся таракан.

 

Пока они будут думать, обойти можно примерно так, для Q16.15:

float corrected_fract16_to_float (fract16 f)
{
    int     s, e, m;
    short   x = (short) f;

    if (x == 0) return (0.0f); // test input is zero

    s = (x & 0x8000) << 16;    // extract sign
    m = (s) ? -x : x;          // mantissa seed (positive)
    e = 0x7F;                  // biased exp seed
    while (!(m & 0x8000))      // find msb
    {
        e = e -  1;            // decrement exp
        m = m << 1;            // shift msntissa
    }
    e = e << 23;               // shift exp in place
    m = (m & 0x7FFF) << 8;     // remove mantissa implicit bit
    m = (s | e | m);           // assemble result
    
    return (*(float *)&m);     // done
}

Ради любопытства, вы можете распечатать на 4 чиcла больше:

    float r;

    r  = fract16_to_float (0x0000);
    printf ("zero 0x%8X \n", *(int *)&r);

    r  = fract16_to_float (0x8000);
    printf("neg one 0x%8X \n", *(int *)&r);

    r  = fract16_to_float (0x4000);
    printf ("half 0x%8X \n", *(int *)&r);

    r  = fract16_to_float (0x2000);
    printf ("quarter 0x%8X \n", *(int *)&r);

    r  = fract16_to_float (0x1000);
    printf ("eighth 0x%8X \n", *(int *)&r);

P.S. изменение _MAX_FLOAT_RANGE не поможет, проблема в

неправильном округлении а не в диапазоне входных чисел.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Однажды я столкнулся с подобным при тестировании FastMath - симулятора

арифметики с плавающей точкой для C62xx/C64xx. Все работало неплохо,

вот только 2.0 * 0.0 = 2.0. Такой вот специфичный баг. Оказалось, что

программист слегка промахнулся при вычислении экспоненты произведения.

 

В вашем случае что-то похожее, только программист, по-видимому,

перестарался с обработкой ошибки округления в преобразовании,

а её тут вообще нет, любое Q16.xx _точно_ содержится в 32-битном

float. Напишите в Аналог, что у них завелся таракан.

 

Пока они будут думать, обойти можно примерно так, для Q16.15:

float corrected_fract16_to_float (fract16 f)
{
    int     s, e, m;
    short   x = (short) f;

    if (x == 0) return (0.0f); // test input is zero

    s = (x & 0x8000) << 16;    // extract sign
    m = (s) ? -x : x;          // mantissa seed (positive)
    e = 0x7F;                  // biased exp seed
    while (!(m & 0x8000))      // find msb
    {
        e = e -  1;            // decrement exp
        m = m << 1;            // shift msntissa
    }
    e = e << 23;               // shift exp in place
    m = (m & 0x7FFF) << 8;     // remove mantissa implicit bit
    m = (s | e | m);           // assemble result
    
    return (*(float *)&m);     // done
}

Ради любопытства, вы можете распечатать на 4 чиcла больше:

    float r;

    r  = fract16_to_float (0x0000);
    printf ("zero 0x%8X \n", *(int *)&r);

    r  = fract16_to_float (0x8000);
    printf("neg one 0x%8X \n", *(int *)&r);

    r  = fract16_to_float (0x4000);
    printf ("half 0x%8X \n", *(int *)&r);

    r  = fract16_to_float (0x2000);
    printf ("quarter 0x%8X \n", *(int *)&r);

    r  = fract16_to_float (0x1000);
    printf ("eighth 0x%8X \n", *(int *)&r);

P.S. изменение _MAX_FLOAT_RANGE не поможет, проблема в

неправильном округлении а не в диапазоне входных чисел.

 

 

Спасибо.

Вывод данных:

 

r = fract16_to_float(0x0000);

printf ("zero 0x%8X \n", *(int *)&r);

r = fract16_to_float(0x8000);

printf ("zero 0x%8X \n", *(int *)&r);

r = fract16_to_float(0x4000);

printf ("zero 0x%8X \n", *(int *)&r);

r = fract16_to_float(0x2000);

printf ("zero 0x%8X \n", *(int *)&r);

r = fract16_to_float(0x1000);

printf ("zero 0x%8X \n", *(int *)&r);

 

r = fract16_to_float(0x0000);

printf ("zero 0x%8X \n", *(unsigned short *)&r);

r = fract16_to_float(0x8000);

printf ("zero 0x%8X \n", *(unsigned short *)&r);

r = fract16_to_float(0x4000);

printf ("zero 0x%8X \n", *(unsigned short *)&r);

r = fract16_to_float(0x2000);

printf ("zero 0x%8X \n", *(unsigned short *)&r);

r = fract16_to_float(0x1000);

printf ("zero 0x%8X \n", *(unsigned short *)&r);

 

zero 0xB6980000

zero 0xBF800000

zero 0x3EFFFF1C

zero 0x3E7FFE84

zero 0x3DFFFD54

 

zero 0x 0

zero 0x 0

zero 0x FF1C

zero 0x FE84

zero 0x FD54

 

Вопрос по _MAX_FLOAT_RANGE возник вот почему:

пределы чисел и преобразования определены так

 

/*

* The following constants describe the properties of a

* scaled floating point range, equivalent to the range

* for fract16 values between 0x8000 and 0x7fff

* ==>> DO NOT MODIFY

*/

#define _MIN_FRACT_RANGE ( -1.0 )

#define _MAX_FRACT_RANGE ( 0.999969 )

#define _RANGE_FRACT ( -_MIN_FRACT_RANGE + _MAX_FRACT_RANGE )

 

/*

* The following data are to be supplied by the user.

* They describe the original data set

* ==>> TO BE MODIFIED

*/

#define _MIN_FLOAT_RANGE ( -1.0 )

#define _MAX_FLOAT_RANGE ( 0.99996 )

#define _RANGE_X ( -_MIN_FLOAT_RANGE + _MAX_FLOAT_RANGE )

 

/*

* The following macros are used by the conversion functions

* ==>> DO NOT MODIFY

*/

#define _TERM_A (_RANGE_FRACT / _RANGE_X)

#define _TERM_B ((-_MIN_FLOAT_RANGE * _TERM_A) + _MIN_FRACT_RANGE )

#define _TERM_a (_RANGE_X / _RANGE_FRACT)

#define _TERM_b ((-_MIN_FRACT_RANGE * _TERM_a) + _MIN_FLOAT_RANGE )

#define _INV_2_POW_15 (1.0 / 32768.0)

 

/* Function to convert floating point data into fract16 data */

#pragma inline

#pragma always_inline

fract16 float_to_fract16(float _x)

{

float val = ( ( (_x * _TERM_A) + _TERM_B ) * 32768.0 );

return (fract16)val;

}

 

 

/* Function to convert fract16 data into floating point data */

#pragma inline

#pragma always_inline

float fract16_to_float(fract16 _x16)

{

return ( (((double)_x16 * _TERM_a) * _INV_2_POW_15) + _TERM_b );

}

 

т.е. диапазон float и fract16 не совпадают, что приводит к масштабированию и сдвигу числа при преобразовании.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

[snip] диапазон float и fract16 не совпадают, что приводит к масштабированию и сдвигу числа при преобразовании.

 

0: 0xB6980000 должно быть 0x00000000

-1: 0xBF800000 должно быть 0xBF800000

1/2: 0x3EFFFF1C должно быть 0x3F000000

1/4: 0x3E7FFE84 должно быть 0x3E800000

1/8: 0x3DFFFD54 должно быть 0x3E000000

 

Из пяти чисел только одно преобразуется без ошибки, -1.

Что и следует из применённого метода. Сценарий из логики

кода, на мой взгляд, довольно странный: исходные данные

в плавающей точке, принадлежащие произвольному интервалу,

приводятся к фиксированной точке на интервале [-1,1),

обрабатываются в Q16.15 и преобразуются обратно к исходному

интервалу. Что влечёт огрубление и без того ошибочных исходных

данных, увеличение погрешности вследствие произведённых

арифметических операций в фиксированной точке и, наконец,

дополнительная ошибка при обратном преобразовании в

плавающую точку.

 

Более логичным представляется сценарий "наоборот": исходные

данные на интервале [-1,1) в Q16.15 - например, отсчёты АЦП.

Эти данные преобразуются к плавающей точке, тем самым повышается

точность представления чисел, считаются в плавающей точке, нормируя

результаты так, что бы они (по возможности) не выходили за

пределы исходного интервала и окончательный результат преобразуется

к фиксированной точке - (огрубляя его, конечно) - например для

вывода в ЦАП. При таком методе приведение интервалов не нужно и

преобразование fract16 -> float будет точным. Обратное, в общем,

неверно, но числа, имеющие точное представление в Q16.15 [0, +/-1/2,

+/-1/4, +/-(1/2 + 1/4), ..., -1] будут преобразованы точно.

 

Конечно, считать float на целочисленной машине медленно, но не

в этом же дело, в самом деле? Лучше медленно с неотрицательной

дисперсией, чем быстро с новым словом в статистике и двойкой

в зачётке :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

0: 0xB6980000 должно быть 0x00000000

-1: 0xBF800000 должно быть 0xBF800000

1/2: 0x3EFFFF1C должно быть 0x3F000000

1/4: 0x3E7FFE84 должно быть 0x3E800000

1/8: 0x3DFFFD54 должно быть 0x3E000000

 

Из пяти чисел только одно преобразуется без ошибки, -1.

Что и следует из применённого метода. Сценарий из логики

кода, на мой взгляд, довольно странный: исходные данные

в плавающей точке, принадлежащие произвольному интервалу,

приводятся к фиксированной точке на интервале [-1,1),

обрабатываются в Q16.15 и преобразуются обратно к исходному

интервалу. Что влечёт огрубление и без того ошибочных исходных

данных, увеличение погрешности вследствие произведённых

арифметических операций в фиксированной точке и, наконец,

дополнительная ошибка при обратном преобразовании в

плавающую точку.

 

Более логичным представляется сценарий "наоборот": исходные

данные на интервале [-1,1) в Q16.15 - например, отсчёты АЦП.

Эти данные преобразуются к плавающей точке, тем самым повышается

точность представления чисел, считаются в плавающей точке, нормируя

результаты так, что бы они (по возможности) не выходили за

пределы исходного интервала и окончательный результат преобразуется

к фиксированной точке - (огрубляя его, конечно) - например для

вывода в ЦАП. При таком методе приведение интервалов не нужно и

преобразование fract16 -> float будет точным. Обратное, в общем,

неверно, но числа, имеющие точное представление в Q16.15 [0, +/-1/2,

+/-1/4, +/-(1/2 + 1/4), ..., -1] будут преобразованы точно.

 

Конечно, считать float на целочисленной машине медленно, но не

в этом же дело, в самом деле? Лучше медленно с неотрицательной

дисперсией, чем быстро с новым словом в статистике и двойкой

в зачётке :)

 

Немного ошибся в пердыдущем посте

r = fract16_to_float(0x0000);

r1 = float_to_fract16®;

printf ("zero 0x%8X \n", *(unsigned short *)&r1);

r = fract16_to_float(0x8000);

r1 = float_to_fract16®;

printf ("zero 0x%8X \n", *(unsigned short *)&r1);

r = fract16_to_float(0x4000);

r1 = float_to_fract16®;

printf ("zero 0x%8X \n", *(unsigned short *)&r1);

r = fract16_to_float(0x2000);

r1 = float_to_fract16®;

printf ("zero 0x%8X \n", *(unsigned short *)&r1);

r = fract16_to_float(0x1000);

r1 = float_to_fract16®;

printf ("zero 0x%8X \n", *(unsigned short *)&r1);

 

zero 0x 0

zero 0x 8000

zero 0x 4000

zero 0x 2000

zero 0x 1000

 

Насчет кода преобразования, так он стандартный (для Analog).

Считать с плавующей точкой не успею, т.к. цикл 5 мс, а данных обработать надо много.

Буду думать.

 

Кстати при изменении даипазона _MAX_FLOAT_RANGE = 0.999969

 

Вывод меняется на более вменяемый:

zero 0x 0

zero 0xBF800000

zero 0x3F000000

zero 0x3E800000

zero 0x3E000000

 

Интересно диапазон изменен Analog-ом с какой-то целью или нет?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Считать с плавующей точкой не успею, т.к. цикл 5 мс, а данных обработать надо много.

fastfloat видели? Может подойдет для ваших целей.

А вообще вы точно уверены что выбрали (если конечно выбирали вы) правильный процессор?

Интересно диапазон изменен Analog-ом с какой-то целью или нет?

Спросите у support-а, он у них весьма адекватный.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну, индусы в Бангалоре так видят ;-) Методически последовательно, а для каких-то практических целей вполне адекватно.

Мало ли что Вы ожидаете, что результат >=0? Там ошибка округления. Интерпретируйте как

if (x<0) x=0 Кстати при вычислении во float тоже легко можно получить отрицательный результат, там где ожидается положительный. (S+a*a) - S легко может оказаться меньше 0 и вызвать изумление.

 

Я вот что подумал. Зачем Вам вообще тот fixed-tо-float. Очевидно, что правильный fixed_to_float(X) - это

X/32768. Где X интерпретируется просто как целое число. Труднее обойтись без деления, немного подумать надо

В fastfloat32 (fastfloat16) индусы, если не ошибаюсь, обошлись )) Но underflow (потерю точности) игнорируют, зачем обработка потери точности в программе риал-тайм? Не пересчитывать же всё заново, когда всё пропало B)

Разве что при отладке...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

(S+a*a) - S легко может оказаться меньше 0 и вызвать изумление.

Точнее сказать, что результат может отклонится от ожидаемого

на малую величину (для устойчивого алгоритма). Следовательно,

отрицательное число может появиться _только_ если результат

ожидается равным нулю. Если ожидается число не равное нулю,

то знак результата _должен_ совпадать со знаком ожидаемого

числа. "Это элементарно в стандарте IEEE754" :)

 

Например, (S+a*a) - S:

 

Как обычно, eps = min(x): x > 0 и 1 + x > 1, представимое в

формате плавающей точки.

 

1. Допустим что a*a < eps*|S|. Следовательно (S+a*a) == S

и значение выражения точно равно нулю. Если |S| == 0 или

eps*|S| денормализованное, т.е. округляется до нуля, то

из неотрицательности a*a (что гарантируется в IEEE754) следует,

что a*a == 0 и значение выражения остаётся равным нулю.

 

2. Допустим что a*a >= eps*|S|. Следовательно a*a > 0 и (S+a*a) > S.

Отсюда следует, что независимо от знака S значение выражения > 0.

Изменено пользователем AndrewN

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...