Visual DSP定点数(fract)使用指南

fract数据类型(Fractional Data Type

C类型

用法

fract16

16位有符号定点数,1.15

fract32

32位有符号定点数1.31

fract2x16

16位有符号定点数

这几个数据类型实际上并不是固有的数据类型,而是使用typedef语句将short定义成fract16,long定义成fract32fract2x16。因此,你不能直接通过fract16*fract16来得到正确的定点相乘结果。对于C++,有两个类”fract””shortfract”定义了基本的算术操作。以下仅谈在C中的操作。

内建函数(Built-in Functions

由于fract算术的特殊性,系统提供了一套接口用于实现这些运算,它们定义在fract.h文件中。所有在fract.h中的文件都被标记为“内联”的,所以如果开了编译器优化,内建函数将会被展开。下面给出的内建函数并不全,更多的请参考[1]

fract16 内建函数

fract16 add_fr1x16(fract16 f1, fract16 f2);

       执行16位加法(f1 + f2)

fract16 sub_fr1x16(fract16 f1, fract16 f2);

       执行16位减法(f1 – f2)

fract16 mult_fr1x16(fract16 f1, fract16 f2)

       执行16位乘法(f1 * f2),结果截短到16

fract16 multr_fr1x16(fract16 f1, fract16 f2)

       执行16位乘法,结果舍入到16位。是否进位根据ASTAT寄存器的RND_MOD是否被设置。

fract32 mult_fr1x32(fract16 f1, fract16 f2)

       执行16位乘法,返回32位结果。

fract16 abs_fr1x16(fract16 f1)

       返回输入参数的绝对值,如0x8000返回0x7fff

fract16 min_fr1x16(fract16 f1, fract16 f2)

       返回f1,f2的较小值

fract16 max_fr1x16(fract16 f1, fract16 f2)

       返回f1,f2的较大值

fract16 negate_fr1x16(fract16 f1)

       返回-f1

fract16 shl_fr1x16(fract16 src, short shft)

       src作算术移位,shft为正则向左移,保留符号位,空的位用0填充;shft为负则向右移,符号扩展;

fract32 内建函数

fract32 add_fr1x32(fract32 f1,fract32 f2)

       返回f1 + f2

fract32 sub_fr1x32(fract32 f1,fract32 f2)

       返回f1 – f2

fract32 mult_fr1x32x32(fract32 f1,fract32 f2)

       返回f1*f2,运算的中间结果保存在40位的累加寄存器,然后舍入到32位。

fract32 multr_fr1x32x32(fract32 f1,fract32 f2)

       和上一个函数一样,只不过提供了额外的舍入精度

fract32 mult_fr1x32x32NS(fract32 f1, fract32 f2)

       提供不饱和的乘法f1 * f2,这个相对来说会快一些

fract32 abs_fr1x32(fract32 f1)

       返回f1的绝对值

fract32 min_fr1x32(fract32 f1, fract32 f2)

       返回f1, f2的较小值

fract32 max_fr1x32(fract32 f1, fract32 f2)

       返回f1, f2的较大值

fract32 negate_fr1x32(fract32 f1)

       返回-f1

fract16 sat_fr1x32(fract32 f1)

       如果f1 > 0x00007fff,则返回0x7fff,如果f1< 0xffff8000,则返回0x8000,其他情况,返回f1的低16

 

fract2x16 内建函数

将两个fract16打包成fract2x16表示为{a,b}”a”为高半部,”b”为低半部

fract2x16 compose_fr2x16(fract16 f1, fract16 f2)

f1,f2打包成fract2x16的类型

输入:2fract16

输出:{f1, f2}

fract16 high_of_fr2x16(fract2x16 f)

返回f的高半部

输入:{a, b}

输出:a

fract16 low_of_fr2x16(fract2x16 f)

返回f的低半部

输入:{a, b}

输出:b

fract2x16 add_fr2x16(fract2x16 f1,fract2x16 f2)

输入:f1{a, b}, f2{c, d}

输出:{a + c, b + d}

fract2x16 sub_fr2x16(fract2x16 f1,fract2x16 f2)

输入:f1{a, b}, f2{c, d}

输出:{a – c, b – d}

fract2x16 mult_fr2x16(fract2x16 f1,fract2x16 f2)

输入:f1{a, b}, f2{c, d}

输出:{trunc16(a*c), trunc16(b*d)}

fract2x16 multr_fr2x16(fract2x16 f1,fract2x16 f2)

输入:f1{a, b}, f2{c, d}

输出:{round16(a*c), round16(b*d)}

fract2x16 negate_fr2x16(fract2x16 f1)

输入:f1{a, b}

输出:{-a, -b}

fract2x16 shl_fr2x16(fract2x16 f1,short shft)

输入:f1{a, b} shft

输出:{a << shft, b << shft}

fract16 sum_fr2x16(fract2x16 f1)

输入:f1{a, b}

输出:a + b

fract2x16 add_as_fr2x16(fract2x16 f1,fract2x16 f2)

输入:f1{a, b}, f2{c, d}

输出:{a + c, b – d}

fract16fract2x16的一点注解

当编译使用单数据fract16运算的程序时,编译器会尝试优化,寻找可以并行计算的情形,因此重写程序以显式使用fract2x16并不总是能产生性能提高。

fract数据类型的应用(Application of Fractional Data Type)

fract常量的表示

要对一个fract类型(fract16, fract32)赋常量值应该使用r16,r32后缀。

例如:

要为fract16类型的变量a赋值0.5,那么可以使用

a=0x4000a=0.5r16

两种方式,显然,后者看起来更直观。

分数与浮点数之间的转换

VisualDSP++运行时库提供了高层次的支持用于fract与浮点数之间的转换。这些函数的声明包含在头文件fract2float_conv.h里。

fract32 fr16_to_fr32(fract16);        //Deposits a fract16 to make a fract32

fract16 fr32_to_fr16(fract32);        //Truncate a fract32 to make a fract16

 

fract32 float_to_fr32(float);           //Convert a float to fract32

fract16 float_to_fr16(float);           //Convert a float to fract16

 

float fr16_to_float(fract16);    //Convert a fract16 to float

float fr32_to_float(fract32);    //Convert a fract32 to float

综合实例

fract转换为float并输出

#include <stdio.h>
#include 
<fract.h>
#include 
<fract2float_conv.h>

int main( void )
{
       fract16 x
=0.4r16;
       
float fx;

       fx 
= fr16_to_float(x);
       printf(
"x=%f\n", fx);

       
return 0;
}

 

输出:

       x=0.399994

fractfloat的时间花费对比

代码很简单,这里不再费口舌:

#include <stdio.h>
#include 
<fract.h>
#include 
<fract2float_conv.h>
#include 
<time.h>
#define COUNT 10000

int main( void )
{
       
int i;
       fract32 x, y, z;
       
float fx, fy, fz;
       clock_t t1, t2;    

       
//test fract,be sure that the sum won't overflow
       t1 
= clock();
       x 
= 0; y = 0; z = 0;
       
for (i = 0; i < COUNT; i++)
       {
              z 
= add_fr1x32(z, mult_fr1x32x32NS(x, y));
              x 
= add_fr1x32(x, 0.000001r32);
              y 
= add_fr1x32(y, 0.000001r32);
       }
       t2 
= clock();
       printf(
"result:%f\n", fr32_to_float(z));
       printf(
"fract operations cost:%uk Cycles\n", (t2 - t1) / 1000);

       
//test float
       t1 
= clock();
       fx 
= 0; fy = 0; fz = 0;
       
for (i = 0; i < COUNT; i++)
       {
              fz 
+= fx * fy;
              fx 
+= 0.000001;
              fy 
+= 0.000001;
       }
       t2 
= clock();
       printf(
"result:%f\n", fz);
       printf(
"float operations cost:%uk Cycles\n", (t2 - t1) / 1000);
       
return 0;
}

 

输出(DEBUG模式)

       result:0.333131

       fract operations cost:360k Cycles

       result:0.333240

       float operations cost:5148k Cycles

输出(RELEASE模式)

       result:0.333131

       fract operations cost:90k Cycles

       result:0.333240

       float operations cost:5018k Cycles

这里可以看到性能的差异还是很大的。在RELEASE下,性能相差50倍以上。不过这里有一点值得注意,两个运算的结果并不完全一样,相差得有点大。这里没法给出详细的理论分析差异的原因,个人猜想是由于误差累积差成的。因此,在实际应用中,最好注意下不要这种连续累加的情况。使用multr_fr1x32x32结果会更接近一些,不过也差不了多少。

扩展阅读(Further Reading)

参考[1]有关于C++操作fract的详细细节,以及复数fract内建函数的使用,有兴趣的读者请自行了解。这些都在[1]Compiler->C/C++ Compiler Language->Compiler Built-In Functions章节。还有一些有关映射ETSI(European Telecommunications Standards Institute) fract函数到内建函数的资料,本人也没用过。

参考(References

[1]“Visual DSP++ 5.0 Compiler and Library Manual for Blackfin Processors”

posted @ 2011-05-10 15:55  枫叶落一地  阅读(3589)  评论(0编辑  收藏  举报