如何以确定的方式将IEEE754浮点数转换为定点?

时间:2012-07-18 13:15:48

标签: c# floating-point ieee-754 fixed-point deterministic

我需要将32位IEEE754浮点数转换为带符号的Q19.12定点格式。问题在于它必须以完全确定的方式完成,因此通常的(int)(f *(1<<<<<<<<<<<<<<<<<<<<<< FRACTION_SHIFT))不合适是否有任何“小小摆弄”或类似的确定性转换方法?

编辑:在这种情况下确定性假定为:给定相同的浮点数据在不同平台上实现完全相同的转换结果。

3 个答案:

答案 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,它保证了决定论。它非常不完整,但您需要的部分已经实现。

相关问题