C++:模拟定点除法/乘法

Posted

技术标签:

【中文标题】C++:模拟定点除法/乘法【英文标题】:C++: Emulated Fixed Point Division/Multiplication 【发布时间】:2011-02-17 12:35:55 【问题描述】:

我正在编写一个 Fixedpoint 类,但遇到了一些问题……乘法、除法部分,我不知道如何模拟。我对除法运算符进行了非常粗暴的抨击,但我确信这是错误的。到目前为止,它是这样的:

class Fixed

    Fixed(short int _value, short int _part) : 
        value(long(_value + (_part >> 8))), part(long(_part & 0x0000FFFF)) ;

    ...

    inline Fixed operator -() const  // example of some of the bitwise it's doing
    
        return Fixed(-value - 1, (~part)&0x0000FFFF);
    ;

    ...

    inline Fixed operator / (const Fixed & arg) const // example of how I'm probably doing it wrong
    
        long int tempInt = value<<8 | part;
        long int tempPart = tempInt;
        tempInt  /= arg.value<<8 | arg.part;
        tempPart %= arg.value<<8 | arg.part;
        return Fixed(tempInt, tempPart);
    ;

    long int value, part; // members
;

我……不是一个很好的程序员,哈哈!

该类的“部分”为 16 位宽(但表示为长 32 位,因为我认为在修复之前需要为可能的溢出留出空间),“值”也是如此,它是整数部分.当“部分”在其中一个操作中超过 0xFFFF 时,最高 16 位被添加到“值”中,然后该部分被屏蔽,因此仅保留最低 16 位。这是在初始化列表中完成的。

我不想问,但如果有人知道我在哪里可以找到类似这样的文档,甚至只是“技巧”或如何执行这两个运算符,我会非常高兴!在数学方面我是个笨蛋,我知道以前有人必须这样做/问过这个问题,但是搜索谷歌并没有将我带到应许之地...

【问题讨论】:

这是作业吗?您实际上是在尝试模仿浮点数学,但不使用浮点字节表示法?你看过 IEEE 是如何处理这个问题的吗? @Zac 遗憾的是,没有,从来没有上过编程学校,或者你可以说什么都没有!这是为了在我正在做的 2d 物理引擎中避免浮点算术的一个非常糟糕的尝试。但我现在正在谷歌搜索 IEEE,所以我会看到的! @Zac:OP 是在谈论“固定”点 mul&division,而不是“浮动”点,这是有区别的。 en.wikipedia.org/wiki/Fixed-point_arithmetic @Tony:两者背后的基本原理是一样的。但是,除非在没有 FPU 的系统上使用它,否则它的效率可能低于仅使用浮点数。 @ZacHowland 实际上不是。在具有 FPU 的系统上,简单的整数运算可能仍然比浮点运算更快。此外,可能需要更高的精度而不是更宽的范围。例如,当 24 位单精度有效数有点不足,但对双精度的操作比 32 位 int 长时 【参考方案1】:

正如 Jan 所说,使用单个整数。由于看起来您指定的是 16 位整数和小数部分,因此您可以使用普通的 32 位整数来执行此操作。

“诀窍”是在对数字进行操作时了解数字的“格式”会发生什么。您的格式将被描述为 16.16。当您添加或减去时,格式保持不变。当你乘法时,你会得到 32.32——所以你需要一个 64 位的临时值作为结果。然后你做一个 >>16 的移位来降低到 48.16 格式,然后取底部 32 位在 16.16 中得到你的答案。

我对分割有点生疏——在 DSP 中,我学到了这些东西,我们尽可能避免(昂贵的)分割!

【讨论】:

这听起来很对!好的,然后只使用一个 32 位整数,不知道为什么我一开始不使用它。而且......啊哈,你让乘法部分看起来很轻松。我在想这将是一个完整的考验。我想现在只是为我找到分区部分的问题。谢谢! @Clairvoire:article that Zac linked to 在更一般的情况下给出了除法运算的详细信息,其中两个操作数的乘法因子都不是 2^16。这应该足以让你继续前进。【参考方案2】:

我建议使用一个整数值而不是单独的整数和小数部分。加法和减法是直接对应的整数,您可以简单地使用 64 位支持,现在所有常见的编译器都有:

乘法:

operator*(const Fixed &other) const 
    return Fixed((int64_t)value * (int64_t)other.value);

部门:

operator/(const Fixed &other) const 
    return Fixed(((int64_t)value << 16) / (int64_t)other.value);

64 位整数是

在 gcc 上,stdint.h(或cstdint,将它们放在std:: 命名空间中)应该可用,因此您可以使用我上面提到的类型。否则,在 32 位目标上为 long long,在 64 位目标上为 long。 在 Windows 上,始终为 long long__int64

【讨论】:

这搞乱了定点,你需要在乘法之后进行位移才能将其恢复。 我应该指定,但我希望除法的行为类似于正常除法的工作方式,而不丢弃剩余部分,但这确实给了我一个想法!【参考方案3】:

要启动并运行,首先实现(一元)inverse(x) = 1/x,然后将a/b 实现为a*inverse(b)。您可能希望将中间体表示为 32.32 格式。

【讨论】:

从来没有想过那个操作...试图让我的头脑围绕一个人会如何做到这一点,但它需要以任何一种方式进行划分!感谢您的提醒! 最简单的解决方案是通过(1&lt;&lt;31)/(x*65536) &lt;&lt; 1。无论如何,第一部分是一个常数,(x*65536) 只是将您的 16.16 fp 数字重新解释为 32 位整数。结果将是一个 32 位整数 (1/x) * 65536,您可以轻松地将其重新解释为 16.16 的逆。缺点:&lt;&lt;31 / &lt;&lt;1 表示 LSB 丢失。

以上是关于C++:模拟定点除法/乘法的主要内容,如果未能解决你的问题,请参考以下文章

使用定点的复数除法

如何比较 C++ 中 log() 和 fp 除法的性能?

C中的定点无符号除法

高精度加法,减法,乘法,除法

Excel怎么进行乘法和除法

高精度乘法和除法