我的定点算术的实现是否正确?

neu*_*rte 1 c++ fixed-point

不久前,我创建了一堆用于定点值操作的C宏.在SO的几个问题和答案的鼓励下,我希望在我的计划的计算密集部分中获得性能提升.虽然代码似乎产生了正确的结果,但我想知道它是不是太天真/过于简单,因为它实际上比我的例程的常规浮点版本运行得慢(我在Wintel上进行双三次图像插值).您能否看一下包含我的宏的这段代码,并提出一些改进建议,特别是在性能方面?谢谢.

// these are architecture-dependent
typedef short int fixed16;
typedef int fixed32;
typedef __int64 fixed64;

// value of 2^n
#define POW2(n) (1 << n)

// create 16bit integer-based fixed point value from a floating point value, n is the number of bits reserved for the fractional part
#define FP_MAKE16(x, n) ((x) > 0.0 ? static_cast<fixed16>(floor((x) * POW2(n) + 0.5)) : static_cast<fixed16>(ceil((x) * POW2(n) - 0.5)))
// the same, 32bit
#define FP_MAKE32(x, n) ((x) > 0.0 ? static_cast<fixed32>(floor((x) * POW2(n) + 0.5)) : static_cast<fixed32>(ceil((x) * POW2(n) - 0.5)))
// and 64bit
#define FP_MAKE64(x, n) ((x) > 0.0 ? static_cast<fixed64>(floor((x) * POW2(n) + 0.5)) : static_cast<fixed64>(ceil((x) * POW2(n) - 0.5)))

// convert a fixed-point integer from one (n) format to another (m) assuming n < m
#define FP_CONVERT_UP(x, n, m) ((x) << (m-n))
// same for n > m
#define FP_CONVERT_DOWN(x, n, m) ((x) >> (n-m))

// convert floating-point value back to float
#define FP_FLOAT(x, n) (static_cast<float>(x) / POW2(n))
// same for double 
#define FP_DOUBLE(x, n) (static_cast<double>(x) / POW2(n))
// and for int. fractional part will be discarded! 
#define FP_INT(x, n) ((x) >> n)

// arithmetic operations for same-format numbers 
#define FP_NEG(a) ((~a)+1)
#define FP_ADD(a, b) ((a) + (b))
#define FP_SUB(a, b) ((a) + FP_NEG(b))
#define FP_MUL(a, b, n) (((a) * (b)) >> n)
#define FP_DIV(a, b, n) (((a) << n) / (b))
#define FP_POW2(a, n) (((a) * (a)) >> n)
#define FP_POW3(a, n) (((((a) * (a)) >> n)*(a)) >> n)

// arithmetic for different-format numbers, assuming n is the target (result) format and n > m
#define FP_ADD_UP(a, b, n, m) ((a) + ((b) << (n-m)))
#define FP_SUB_UP(a, b, n, m) ((a) + FP_NEG((b) << (n-m)))
#define FP_MUL_UP(a, b, n, m) (((a) * (b)) >> m)
#define FP_DIV_UP(a, b, n, m) (((a) << m) / (b))

// same for n < m
#define FP_ADD_DOWN(a, b, n, m) ((a) + ((b) >> (m-n)))
#define FP_SUB_DOWN(a, b, n, m) ((a) + FP_NEG((b) >> (m-n)))
#define FP_MUL_DOWN(a, b, n, m) (((a) * (b)) >> m)
#define FP_DIV_DOWN(a, b, n, m) (((a) << m) / (b))
Run Code Online (Sandbox Code Playgroud)

编辑:基本上,答案和评论转向了这两点:

  • 代码很可怕,难以使用和维护:我完全同意并且会花时间将它封装在一个类中.我对性能表示担忧,并且我确信它们没有根据,我一无所获.:)
  • 定点不值得麻烦:虽然在我的平台上可能是这样,但我创建它是为了提高我的代码在没有浮点硬件的平台上的执行速度.在那里,浮点运算花了太长时间,固定是要走的路.我只是在英特尔测试这个,因为我目前无法访问目标硬件

虽然我非常感谢迄今为止所提供的见解,但我希望能听到一些实际上在定点上进行一些计算的人告诉我这些算术运算是否确实如此.也许还有一些我不知道的额外的漂亮比特,这在性能或精度方面有所不同?换句话说,如果我要封装此代码,我可以在内联运算符函数中保持相同的算术指令基本上与现在相同,还是应该以某种方式更改它们?

Pup*_*ppy 5

内联函数.使用它们.不是宏.使用一个类,重载它的运算符.并且static_cast在C中不存在.这段代码非常糟糕,如果您使用它发布了代码示例,那将完全不可读.

请记住,浮点运算是在硬件中实现的,而您实现的定点运算是在软件中.这种变化会受到惩罚,很可能很容易就是你的代码在算法层面上的速度不够快,无法克服这种变化.

  • @neuviwmeporte:浮点乘法是一个硬件操作.int-int乘法然后int-shift是更多的寄存器(三个操作数,而不是两个),并且你要调用两次硬件.如果int-int mul比float-float mul快,那么在许多情况下它是没有价值的,因为你等待额外的内存或浪费时间,而CPU等待下一个周期执行下一个操作.你也在吹更多的指令缓存.在编写基于它们的代码之前,永远不要"计算"任何东西,*简介*并证明您的假设. (2认同)