【问题标题】:convert unsigned int to single precision floating point with only integer operations仅使用整数运算将 unsigned int 转换为单精度浮点
【发布时间】:2013-03-15 07:16:59
【问题描述】:

这个想法是为了做和这个几乎一样的事情 http://www.h-schmidt.net/FloatConverter/IEEE754.html

例如,如果我插入 357,我应该得到 0x43b28000

float unsignedToFloat( unsigned int x ) {
unsigned int result = 0;

return *(float*)&result;
}

但是我该怎么做呢?

Convert ieee 754 float to hex with c - printf

我看到了这个,但似乎没有任何好的解决方案。

【问题讨论】:

  • 如果你使用 C++,你可以使用 reinterpret_cast。
  • 我不明白。您的函数返回一个浮点数。它如何返回0x43b28000?你的意思是它返回357.0? (您链接的第一页在底部回答了这个问题,在他们的常见问题解答中回答了第二个问题。)这个问题毫无意义。这就像是在问如何将“两蘑菇”中的“二”转化为“两车”中的“二”。没什么可做的。
  • 在这种情况下,0x43b28000 是 32 位单的二进制(十六进制)表示。我没有代码,目前没有时间,但如果我稍后有一点时间,我会描述它是如何完成的(如果没有其他人这样做的话)。它只涉及一些移位和一些加/减。
  • @roger_rowland:我的印象是 userXYZ 想知道这是如何工作的,而不是让编译器为他做这件事。
  • @Rudy Velthuis - 好吧。我点击了 OP 帖子中的链接,它描述了 Java 应用程序是如何做到的,这与“reinterpret_cast”的工作原理完全相同。这不是他要问的吗?

标签: floating-point


【解决方案1】:

首先,如果x 为零,则返回零。

接下来找到x中最高位非零位的索引。叫它i

如果 i 小于 24,则将 x 左移 23 - i 以获得归一化的有效数字。现在清除位 23 以隐藏隐式位,并将位 23:30 设置为 127 + i,这是偏置指数。返回结果。

否则,将x 右移i - 23 以通过截断获得归一化的有效位,并清除隐含位并按上述方式设置指数。如果您想要的舍入模式是截断或舍入到负无穷大,那么您就完成了。否则,您将需要查看从x 底部移出的位。如果所需的舍入模式是舍入到无穷大并且设置了任何这些位,则将结果加一并返回。最后,如果所需的舍入模式是 round-to-nearest-ties-to-even(IEEE-754 默认),则有以下三种情况:

  • 尾随位为b0...:返回截断的结果。
  • 尾随位是b1000...:这是一个确切的中途情况。如果我们调用截断结果t,则需要返回t + (t&1);即仅当 t 为奇数时才向上取整。
  • 尾随位为b1...1...:将截断的结果加一并返回。

【讨论】:

    【解决方案2】:

    我是这样做的:

    更新

    这个可以正确处理 0、INT_MIN 和舍入,AFAICT:

    #define SIGN_MASK       (1 << 31)
    #define HIDDEN_MASK     (1 << 23)
    #define MANTISSA_MASK   (HIDDEN_MASK - 1)
    
    #define INT_MIN 0x80000000
    #define INT_MAX 0x7FFFFFFF
    
    float intToFloat(int n)
    {
        int sign;
        int exp;
        unsigned int half;
    
        if (n == 0)
             return 0.0f;
    
        if (n == INT_MIN)
            return -(float)(INT_MIN);
    
        sign = n < 0 ? SIGN_MASK : 0;
        if (sign)
            n = -n;
    
        if (!(n & ~(HIDDEN_MASK | MANTISSA_MASK)))
            for (exp = 0; !(n & HIDDEN_MASK); n <<= 1, exp--) ;
        else
        {
            half = 0;
            for (exp = 0; n & ~(HIDDEN_MASK | MANTISSA_MASK); exp++)
            {
                half >>= 1;
                if (n & 1)
                    half |= 0x80000000;
                n >>= 1;
            }
            if (half > INT_MIN || ((half == INT_MIN) && (n & 1) != 0))
            {
                n++;
                if (n == 0x1000000)
                {
                    n = 0; // or 0x800000, doesn't matter.
                    exp++;
                }
            }
    
        }
    
        exp = (exp + 127 + 23) << 23;
        n = (n & MANTISSA_MASK) | sign | exp;
    
        return *((float *)&n);
    }
    

    【讨论】:

    • 您的舍入逻辑不正确,因为每次从底部移出位时都会重新舍入。这将导致严重的双舍入问题。 (考虑要四舍五入的情况,它们是b011。这应该向下舍入。您的第一个舍入将四舍五入,给出b10,如果低位为截断的结果已设置。有一些方法可以像这样一次进行一点舍入,但是在软件中考虑一次性舍入整个字段要容易得多。
    • nINT_MIN 时,此操作失败。然后-n 溢出(在常见的 C 实现中),n &gt;&gt;= 1 导致问题。另外,为什么要返回*((float *)&amp;n)(C 也不能保证)而不是简单的 0?
    • 你是对的关于 0 和 INT_MIN。然后我会返回常量。
    • @StephenCanon:你是对的。我必须在轮班之后将舍入更新为一次。
    • @StephenCanon:现在就试试吧。我尝试了从 INT_MIN 到 INT_MAX 的所有值,没有发现任何错误。
    猜你喜欢
    • 1970-01-01
    • 2020-02-03
    • 2011-05-19
    • 1970-01-01
    • 2013-04-16
    • 2020-06-13
    • 1970-01-01
    • 2011-11-17
    • 2014-01-30
    相关资源
    最近更新 更多