我需要将32位IEEE754浮点数转换为带符号的Q19.12定点格式。问题在于它必须以完全确定的方式完成,因此通常的(int)(f *(1<<<<<<<<<<<<<<<<<<<<<< FRACTION_SHIFT))不合适是否有任何“小小摆弄”或类似的确定性转换方法?
编辑:在这种情况下确定性假定为:给定相同的浮点数据在不同平台上实现完全相同的转换结果。
答案 0 :(得分:3)
浮点不是非确定性的。你在哪里得到那个荒谬的假设?
扩大一点:
1 << FRACTION_SHIFT
是2的精确幂,因此完全以浮点表示。精确乘以2的乘法是精确的(除非发生上溢/下溢,但在这种情况下,无论如何都没有有意义的定点表示,所以你不在乎)。所以唯一可能的舍入源是转换为整数,它由C#完全指定;因此,不仅结果具有确定性,而且您将获得可移植的相同结果。
答案 1 :(得分:3)
虽然@ StephenCanon的答案可能是正确的,但这个特殊情况是完全确定的,我决定保持更安全的一面,并且仍然手动进行转换。这是我最终得到的代码(感谢@CodesInChaos有关如何执行此操作的指示):
public static Fixed FromFloatSafe(float f) {
// Extract float bits
uint fb = BitConverter.ToUInt32(BitConverter.GetBytes(f), 0);
uint sign = (uint)((int)fb >> 31);
uint exponent = (fb >> 23) & 0xFF;
uint mantissa = (fb & 0x007FFFFF);
// Check for Infinity, SNaN, QNaN
if (exponent == 255) {
throw new ArgumentException();
// Add mantissa's assumed leading 1
} else if (exponent != 0) {
mantissa |= 0x800000;
}
// Mantissa with adjusted sign
int raw = (int)((mantissa ^ sign) - sign);
// Required float's radix point shift to convert to fixed point
int shift = (int)exponent - 127 - FRACTION_SHIFT + 1;
// Do the shifting and check for overflows
if (shift > 30) {
throw new OverflowException();
} else if (shift > 0) {
long ul = (long)raw << shift;
if (ul > int.MaxValue) {
throw new OverflowException();
}
if (ul < int.MinValue) {
throw new OverflowException();
}
raw = (int)ul;
} else {
raw = raw >> -shift;
}
return Fixed.FromRaw(raw);
}
答案 2 :(得分:1)
如果绝对需要确定性,我会将内容解析为整数,并手动进行转换。
首先提取指数。如果返回0
太小,如果它太大,则抛出溢出异常。
接下来提取符号和尾数(记住隐含的前导1
)。如果符号位为1
,则翻转尾数的符号。最后通过指数和偏差执行位移。
我还写了soft float implementation,它保证了决定论。它非常不完整,但您需要的部分已经实现。