正在寻找除以2的最快方法 我已经搜索了半天,发现了一些非常有趣的事情,关于使用定点数据类型和C++中的位移位来完成除法运算,同时避免浮点运算。然而,我只能理解其中的一小部分,我似乎无法让任何东西发挥作用

正在寻找除以2的最快方法 我已经搜索了半天,发现了一些非常有趣的事情,关于使用定点数据类型和C++中的位移位来完成除法运算,同时避免浮点运算。然而,我只能理解其中的一小部分,我似乎无法让任何东西发挥作用,c++,floating-point,fixed-point,C++,Floating Point,Fixed Point,我想做的就是取两个整数,把它们相加,除以2得到平均值。我需要能够很快做到这一点,因为我在Arduino上插值相机像素数据,我还有其他操作要做 所以我对一般的转换感到困惑。假设我想除以2的整数是27。27的一半是13.5。但无论我尝试什么样的定点数据类型,我只能得到13作为输出。例如: uint8_t x = 27; Serial.println( x >> 1 ); 返回13 必须有一些简单的方法来实现这一点,对吗?定点确实为您提供了一种表示13.5的方法。维基百科关于Q数字格

我想做的就是取两个整数,把它们相加,除以2得到平均值。我需要能够很快做到这一点,因为我在Arduino上插值相机像素数据,我还有其他操作要做

所以我对一般的转换感到困惑。假设我想除以2的整数是27。27的一半是13.5。但无论我尝试什么样的定点数据类型,我只能得到13作为输出。例如:

uint8_t  x = 27;
Serial.println(  x >> 1 );
返回13


必须有一些简单的方法来实现这一点,对吗?

定点确实为您提供了一种表示13.5的方法。维基百科关于Q数字格式的文章提供了信息:

可以这样想:您一直使用整数,但不是按面值取整数,而是将它们全部隐式地除以2的幂以获得它们的语义值

因此,如果使用无符号字节作为基类型(值介于0和255之间,包括0和255之间),则可能会隐式除以2**3(8)。现在要表示27,需要将整数设置为27*8=>216。要除以二,将一向右移动;现在你的整数是108,当除以隐式分母8,得到13.5,你期望的值


当然,你必须意识到定点数字系统(当然还有浮点系统,尽管它不太明显)仍然有局限性;无论您执行什么操作,某些操作都会溢出,某些操作会导致精度损失。这是使用有限大小类型的正常结果。

之所以只得到13,是因为在进行位移位时,实际上切断了最低有效位。因为您正在切断它们,所以没有剩余的要检查。如果您对剩余部分感兴趣,您可以执行以下操作:

uint8_t x = 27;
Serial.println((x - (x >> 1) - (x >> 1));
(x-(x>>1))这里应该给出14

一旦确定余数是否为1,就可以很简单地将.5加到一个数字上

假设我想除以2的整数是27。27的一半是13.5。但是 无论我尝试什么样的定点数据类型,我只能得到13作为一个整体 输出

来自维基百科的定点算法:

比例因子通常为10的幂次方(为方便起见)或 2的幂(计算效率)

您实际上提到了定点数据类型,我认为这是最好的方法。但不管你怎么做?也许我们对不动点算法有不同的理解

同时避免浮点运算

另一个值得追求的目标,虽然价值在降低。即使在嵌入式系统中,我也很少需要处理没有浮点部件的处理器。浮点硬件已经相当不错了

无论如何,使用定点可以避免使用浮点。甚至为了展示的目的

我想我需要举几个例子


固定点示例1:美元和便士

美国货币的单位是以美元为基础的。美元是一种固定点数据类型

那么,如果你有27美元,你怎么和你的兄弟姐妹平分呢

你们都知道的一种方法是将27美元兑换成2700便士。将该值除以2很简单。现在你和你的兄弟姐妹每人可以得到1350便士。(即penny是一种定点数据类型,可轻松转换为美元/美元,反之亦然)

请注意,这完全是整数运算。添加2个整数,然后除以2(任何现代编译器都会选择最快的实现..整数除法或右移2)在我的桌面上,这2个操作只需不到一微秒的时间就可以完成

您不应该再浪费时间测量这两个选项的相对性能(divide vs right shift),只要在代码测试正确时启用-O3即可。您的编译器应该能够正确选择

在任何问题中,单位的选择都是基于一个比例因子,该比例因子涵盖了值的范围(在您的问题中)以及单位之间可理解且可快速实现的转换。请注意,uint64_t可以描述大量现金,即使是便士。(向学生挑战。)


一般来说,关于固定点:

给定

想要平均快速地除以2。。。任何比例因子都能满足您的需求吗?我同意


例2-50美分硬币和1美元

例如,我们试试简单的比例因子2,即单位为hu或半单位。(类似于50美分硬币)

这意味着54胡代表27个单位。(也就是说,54枚50美分的硬币加起来27美元)

定点解决方案是缩放整数值以实现所需的算术运算。如果缩放到偶数值,则所有整数将平均除以hu单位


例3-五分镍币和一美元

另一个可能的刻度可能是20,十进制(用于可读性)和二进制(用于性能)。(注意一美元有20个镍币)

现在540代表一个比例27。i、 e.540个刻痕


所有示例都是完整的整数,提供了精确的答案,并且有一个简单的机制将值转换为呈现给用户。i、 e.使用哪一个固定点,转换成便士的类似物,从而转换成1350便士

将便士计数显示为美元

 std::cout << (pennyCount / 100) << "." << (pennyCount % 100) << std::endl;

现在,您的挑战是使其在输出上看起来美观。

以下操作应该有效,并且应该快速:


float y=(x>>1)+(0.5*(x&0x01))

最快的方法是使用普通除法,然后让编译器对其进行优化
uint8_t  x = 27 * 1/hu;   (hu = 1/2)
uint16  x = 27 * 1/tu;  (tu = 1/20)
 std::cout << (pennyCount / 100) << "." << (pennyCount % 100) << std::endl;
 13.50