【问题标题】:How to fix the position of binary point in an unsigned N-bit interger?如何在无符号 N 位整数中固定二进制点的位置?
【发布时间】:2015-02-26 12:10:20
【问题描述】:

我正在开发 C++ 中的定点算法。我知道,对于 N 位整数,定点二进制整数表示为 U(a,b)。例如,对于一个 8 位整数(即 256 个样本),如果我们用 U(6,2) 的形式表示它,则表示二进制点在从形式右侧开始的第 2 位的左侧:

                   b5 b4 b3 b2 b1 b0 . b(-1) b(-2)

因此,它有 6 个整数位和 2 个小数位。在 C++ 中,我知道有一些移位运算符可以使用,但它们基本上用于移位输入流的位,我的问题是,如何定义形式为 fix 或 U(6,2)。所有主要的处理操作都将在小数部分进行,我只是在寻找一种在 C++ 中进行此修复的方法。对此的任何帮助将不胜感激。谢谢!

示例:假设我有一个输入离散信号,x 轴上有 1024 个采样点(现在只是认为这个输入信号来自某个传感器)。每个样本点都有一个特定的幅度。假设时间 2(x 轴)的样本具有 3.67(y 轴)的幅度。现在我有一个变量“int *input;”它采用样本 2,二进制为 0000 0100。所以基本上我想通过在 C++ 中对样本 2 执行 U(5,3) 将其设为 00000.100。这样我就可以对输入采样周期或时间的一部分执行插值操作。

PS - 我不想为此创建一个单独的类或使用外部库。我只想从我的输入信号中取出每 8 位,对其执行 U(a,b) 修复,然后在小数部分完成其余操作。

【问题讨论】:

  • 我知道我可以使用一些移位运算符,但它们基本上用于移位输入流的位 嗯不......这一切都取决于你重载了操作员要做的事情。 x << 1 其中x 是一个 int 将移动 int 的位,不涉及流。
  • 据我了解,我的意思是如果我传递输入信号或样本或比特流。因此,如果特定时刻的输入是一个样本值(每个这样的值的大小为 1 字节,即 8 位),那么我想对输入的每个这样的输入样本执行 U(a,b) 修复。我是编程初学者(C 和 C++)。一般比较习惯在simulink中做这些东西。如有错误请纠正我。
  • 我不明白你在问什么。
  • @DigitalGeeK - 使用定点或浮点总是施加约束。提及您的用例可能有助于选择不会有害的用例。就目前而言,要将无符号 8 位整数转换为 6:2 定点表示 - 您需要做的就是清除最低 2 位。即uint8_t myVal = 255; myVal &= 0x03; 然后您需要将值向右移动两位以获取整数部分 - 当然,您现在已经将范围除以 4。您现在可以表示的最大数字是 63.75 而不是255.跨度>
  • 假设我有一个输入离散信号,在 x 轴上有 1024 个采样点(现在只是认为这个输入信号来自某个传感器)。每个样本点都有一个特定的幅度。假设时间 2(x 轴)的样本具有 3.67(y 轴)的幅度。现在我有一个变量“int *input;”它采用样本 2,二进制为 0000 0100。所以基本上我想通过在 C++ 中对样本 2 执行 U(5,3) 将其设为 00000.100。这样我就可以对输入采样周期或时间的一部分执行插值操作。希望这可以清除它,如果不是请询问。

标签: c++ visual-c++ fixed-point


【解决方案1】:

简答:左移。

长答案:

  1. 定点数存储为整数,通常是int,这是特定平台最快的整数类型。

  2. 不带小数位的普通整数通常称为Q0Q.0QX.0,其中X 是底层存储类型的总位数(通常为int)。

    李>
  3. 在不同的Q.X 格式之间进行转换,左移或右移。例如,要将Q0 中的 5 转换为 Q4 中的 5,请将其左移 4 位,或将其乘以 16。

  4. 通常查找或编写一个小型定点库来进行基本计算会很有用,例如a*b>>q(a<<q)/b。因为你会经常做Q.X=Q.Y*Q.ZQ.X=Q.Y/Q.Z 并且在做计算的时候需要转换格式。正如您可能已经观察到的,使用普通的* 运算符会给您Q.(X+Y)=Q.X*Q.Y,因此为了使结果适合Q.Z 格式,您需要将结果右移(X+Y-Z) 位。

  5. 除法类似,您从标准的/ 运算符获得Q.(X-Y)=Q.X*Q.Y,并以Q.Z 格式获得结果,您在除法之前移动被除数。不同的是,除法是一项昂贵的操作,从头开始编写一个快速的操作并非易事。
  6. 注意您的平台对双字的支持,它会让您的生活变得轻松很多。使用双字运算,a*b 的结果可以是ab 大小的两倍,这样你就不会因为a*b>>c 而失去范围。如果没有双字,则必须限制ab 的输入范围,使a*b 不会溢出。这在您刚开始时并不明显,但很快您会发现您需要更多的小数位或愤怒才能完成工作,您最终需要深入研究处理器 ISA 的参考手册。

示例:

float a = 0.1;// 0.1
int aQ16 = a*65536;// 0.1 in Q16 format
int bQ16 = 4<<16// 4Q16
int cQ16 = a*b>>16 // result = 0.399963378906250Q16 = 26212, 
                   // not 0.4Q16 = 26214 because of truncating error

【讨论】:

  • 我知道“a”是一个浮点数,乘以 2^16,可以将浮点数转换为 Q16 格式。最后你乘以 a *b 并右移 16 得到结果。但是,您能否详细说明您的示例中的“int bQ16 = 4
  • @DigitalGeeK 它只是将 4Q0 转换为 4Q16,左移 16 或乘以 (2^16)。该代码基本上做了 4 件事:1)将浮点数转换为 Q16; 2) 将 int 从 Q0 转换为 Q16; 3)计算Q32中的乘积; 4;将 Q32 转换为 Q16。
  • 好的,在第二个)0.1*65536 = 6553.6,(0.6 会被排除在外吗?因为它是一个整数),第三个)4*65536 = 262144 和第四个)6553.6*262144/65536 = 26214.4,为什么你提到的结果是0.3999699789....截断后,不包括小数部分即0.4的值不应该是26214吗?另一件事是,我如何在定点代表中表示例如 0.001 秒的时间?因为如果分配给整数数据类型,它只需要 0,如果我将 0.001 转换为 Q8.8 即 0.001*256=0.256,再次, int 数据类型会将此视为 0 rite?。所以基本上派系部分丢失了。
  • 对不起,如果我的怀疑是蹩脚的,只是想弄清楚这些东西是如何工作的,以便我在我的算法中使用它。
  • @DigitalGeeK 0.1*65536 = 6553.6 是的,0.6 丢失了。根据您的舍入模式,它可以是 6553 或 6554。在此示例中,它是“舍入到较小的整数”,因此是 6553。然后,您不是在做 6553.6*262144/65536 = 26214.4,而是 6553.0*262144 /65536 = 26212.0。你会看到,每次你这样做,你都会失去一些准确性。然后是关于“Q8.8 中无法表示的小数”的另一个问题:尝试一些大 Q,例如 Q16.16 或 Q8.24。指导原则是,在确定范围后,您可以为该变量设计 Q 格式以最大限度地提高准确性。
【解决方案2】:

如果这是您的问题:

问。我应该将我的定点整数定义为模板U&lt;int a, int b&gt;(int number),还是不定义U(int a, int b)

我认为您对此的回答是:“您要定义采用两个固定二进制点整数的运算符吗?如果是这样,请将它们作为模板。”

如果您不定义运算符,则模板只是有点额外的复杂性。所以我把它排除在外。

但如果您正在定义运算符,则不希望能够添加U&lt;4, 4&gt;U&lt;6, 2&gt;。你会把你的结果定义为什么?如果您尝试这样做,模板会给您一个编译时错误。

【讨论】:

  • 我将使用模板使其适用于任何传入的数据类型。不管怎样,谢谢你的指导。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-12
  • 2015-04-06
  • 2014-10-24
  • 2019-08-20
  • 1970-01-01
  • 2011-03-09
相关资源
最近更新 更多