我需要将 32 位 IEEE754 浮点数转换为有符号的 Q19.12 定点格式。问题是它必须以完全确定的方式完成,所以通常的 (int)(f * (1 << FRACTION_SHIFT)) 是不合适的,因为它使用非确定性浮点数学。是否有任何“位摆弄”或类似的确定性转换方法?
编辑:在这种情况下确定性假设为:给定相同的浮点数据在不同平台上实现完全相同的转换结果。
我需要将 32 位 IEEE754 浮点数转换为有符号的 Q19.12 定点格式。问题是它必须以完全确定的方式完成,所以通常的 (int)(f * (1 << FRACTION_SHIFT)) 是不合适的,因为它使用非确定性浮点数学。是否有任何“位摆弄”或类似的确定性转换方法?
编辑:在这种情况下确定性假设为:给定相同的浮点数据在不同平台上实现完全相同的转换结果。
浮点数不是非确定性的。你从哪里得到这个荒谬的假设?
扩展一点:
1 << FRACTION_SHIFT
是 2 的精确幂,因此以浮点数精确表示。乘以 2 的精确幂是精确的(除非发生上溢/下溢,但在这种情况下,无论如何都没有有意义的定点表示,所以你不在乎)。因此,唯一可能的舍入来源是转换为整数,这完全由 C# 指定;因此,结果不仅是确定性的,而且您将获得可移植的相同结果。
虽然@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);
}
If determinism is absolutely required, I'd parse the content as an integer, and do the conversion manually.
First extract the exponent. If it's too small return 0
, if it's too large, throw an overflow exception.
Next extract sign and mantissa (remember the implicit leading 1
). If the sign bit is 1
, flip the sign of the mantissa. Finally execute a bit shift by the exponent combined with a bias.
I also wrote a soft float implementation, that guarantees determinism. It's pretty incomplete, but the parts you need are implemented.