我正在实现 32 位有符号整数定点算术。范围是从1到-1,INT32_MAX对应于1。我不确定是否要制作INT32_MIN或-INT32_MAX对应于-1,但这暂时放在一边。
我做了一些乘法和舍入操作,如下所示:
#define mul(a, b) ((int64_t)(a) * (b))
Run Code Online (Sandbox Code Playgroud)
#define round(x) (int32_t)((x + (1 << 30)) >> 31)
Run Code Online (Sandbox Code Playgroud)
然后可以使用 找到两个数字的乘积round(mul(a, b))。
当我检查身份时,问题就出现了。主要问题是 1x1 不是 1。它是INT32_MAX-1。这显然是不希望的,因为我想要位精度。我想这会影响其他附近的数字,因此如果操作数都是 ,则修复不是仅加 1 的情况INT32_MAX。另外,-1x-1 不是-1,1x-1 不是-1,并且-1x-1=-1。所以这些身份都站不住脚。
是否有一个简单的解决方案,或者这只是使用定点算术的症状?
在其一般形式中,定点格式将数字x表示为整数x \xe2\x80\xa2 s。通常,s是某个底数b的幂, s = b p。例如,我们可以将一些美元x存储为x \xe2\x80\xa2100,因此 $3.45 可能存储为 345。这里我们可以很容易地明白为什么这被称为 \xe2\x80\x9cfixed-point\xe2\x80 \x9d 格式:存储的数字在概念上将小数点插入为固定位置,在本例中,最右边数字左边两位:\xe2\x80\x9c345\xe2\x80\x9d 在概念上是 \xe2\x80\x9c3。 45\xe2\x80\x9d。(这也可以称为小数点而不是小数点,考虑到基数b不是十的情况。并且p指定插入小数点的位置,从右侧开始p基数b数字。)
\n如果你让INT_MAX代表 1,那么你就隐含地说s = INT_MAX。(并且,由于INT_MAX不是任何其他整数的幂,因此我们有b =INT_MAX和pINT_MAX = 1。)然后 \xe2\x88\x921 必须由 \xe2\x88\x921\xe2\x80\xa2 =表示-INT_MAX。它不会被表示为INT_MIN(除了在古老的 C 实现中,其中INT_MIN= -INT_MAX)。
给定s = INT_MAX,移位 31 位并不是实现乘法的正确方法。给定两个数字x和y以及表示a和b , xy的表示通过将表示a和b相乘并除以s来计算:
移位 31 除以 2 31,因此与除以 不同INT_MAX。此外,硬件上的除法通常很慢。您可能最好选择s = 2 30而不是INT_MAX。然后你可以移动 30 位。
在计算ab / s时,我们经常要进行四舍五入。在除法之前将 \xc2\xbd 添加到乘积是一种舍入方法,但这可能不是您想要的负乘积。如果乘积为负,您可能需要考虑添加 \xe2\x88\x92\xc2\xbd 。
\n