短版检测溢:我如何检测溢出使用定点乘法描述here但对于一个符号的类型?定点乘法
龙版本:
我还是有一些问题的溢出与我Q31.32 fixed point type。为了更容易在纸上创建示例,我使用相同的算法制作了一个更小的类型,即基于sbyte的Q3.4。我认为,如果我能解决Q3.4类型的所有问题,那么同样的逻辑应该适用于Q31.32。
注意,我可以很容易地通过一个16位的整数执行也执行Q3.4乘法,但我这样做,就好像根本不存在,因为对于Q31.32我需要一个128不存在的位整数(并且BigInteger太慢)。
我希望我的乘法饱和度处理溢出,即当溢出发生,其结果是,可以根据操作数的符号表示的最高值或最小值。
这是基本类型是如何表示:
struct Fix8 {
sbyte m_rawValue;
public static readonly Fix8 One = new Fix8(1 << 4);
public static readonly Fix8 MinValue = new Fix8(sbyte.MinValue);
public static readonly Fix8 MaxValue = new Fix8(sbyte.MaxValue);
Fix8(sbyte value) {
m_rawValue = value;
}
public static explicit operator decimal(Fix8 value) {
return (decimal)value.m_rawValue/One.m_rawValue;
}
public static explicit operator Fix8(decimal value) {
var nearestExact = Math.Round(value * 16m) * 0.0625m;
return new Fix8((sbyte)(nearestExact * One.m_rawValue));
}
}
这是我目前如何处理乘法:
public static Fix8 operator *(Fix8 x, Fix8 y) {
sbyte xl = x.m_rawValue;
sbyte yl = y.m_rawValue;
// split x and y into their highest and lowest 4 bits
byte xlo = (byte)(xl & 0x0F);
sbyte xhi = (sbyte)(xl >> 4);
byte ylo = (byte)(yl & 0x0F);
sbyte yhi = (sbyte)(yl >> 4);
// perform cross-multiplications
byte lolo = (byte)(xlo * ylo);
sbyte lohi = (sbyte)((sbyte)xlo * yhi);
sbyte hilo = (sbyte)(xhi * (sbyte)ylo);
sbyte hihi = (sbyte)(xhi * yhi);
// shift results as appropriate
byte loResult = (byte)(lolo >> 4);
sbyte midResult1 = lohi;
sbyte midResult2 = hilo;
sbyte hiResult = (sbyte)(hihi << 4);
// add everything
sbyte sum = (sbyte)((sbyte)loResult + midResult1 + midResult2 + hiResult);
// if the top 4 bits of hihi (unused in the result) are neither all 0s or 1s,
// then this means the result overflowed.
sbyte topCarry = (sbyte)(hihi >> 4);
bool opSignsEqual = ((xl^yl) & sbyte.MinValue) == 0;
if (topCarry != 0 && topCarry != -1) {
return opSignsEqual ? MaxValue : MinValue;
}
// if signs of operands are equal and sign of result is negative,
// then multiplication overflowed upwards
// the reverse is also true
if (opSignsEqual) {
if (sum < 0) {
return MaxValue;
}
}
else {
if (sum > 0) {
return MinValue;
}
}
return new Fix8(sum);
}
这给结果类型的精度内准确,处理大部分溢出案例。例如:它不处理这些数据,例如:
Failed -8 * 2 : expected -8 but got 0
Failed 3.5 * 5 : expected 7,9375 but got 1,5
让我们研究一下乘法是如何发生的。
-8 and 2 are represented as x = 0x80 and y = 0x20.
xlo = 0x80 & 0x0F = 0x00
xhi = 0x80 >> 4 = 0xf8
ylo = 0x20 & 0x0F = 0x00
yhi = 0x20 >> 4 = 0x02
lolo = xlo * ylo = 0x00
lohi = xlo * yhi = 0x00
hilo = xhi * ylo = 0x00
hihi = xhi * yhi = 0xf0
这个总和显然是0,因为所有的项都是0,除了hihi,只有hihi的最低4位用于最后的总和。
我通常的溢出检测魔术在这里不起作用:结果为零,所以结果的符号是没有意义的(例如0.0625 * -0.0625 == 0(通过舍入),0是正的,但操作数的符号不同); hihi的高位也是1111,即使没有溢出也经常发生。
基本上我不知道如何来检测溢出发生在这里。有更通用的方法吗?
谢谢,但我终于想通一切都在我自己身边看到我上面的回复。 – Asik