【问题标题】:Round down float using bit operations in C使用 C 中的位运算向下舍入浮点数
【发布时间】:2013-02-09 23:20:25
【问题描述】:

我正在尝试使用 C 中的位运算来舍入浮点数。 我首先将浮点数转换为无符号整数。 我认为我的策略应该是获取指数,然后将位归零,但我不确定如何编码。这是我到目前为止所拥有的:

float roundDown(float f);
unsigned int notRounded = *(unsigned int *)&f;
unsigned int copy = notRounded;
int exponent = (copy >> 23) & 0xff;
int fractional = 127 + 23 - exponent;
if(fractional > 0){
   //not sure how to zero out the bits. 
   //Also don't know how to deal with the signed part. 

【问题讨论】:

  • floorf()、roundf() 和相关函数可以做到这一点。这是作业吗?这对我来说似乎不太实用。
  • 不,不是作业,我正在尝试学习 C 中的按位运算,这是教科书中的一个练习。我已经为此苦苦挣扎了几个小时,如果我能让它工作就好了。
  • 您可能需要将尾数位全部、部分或不清零,具体取决于指数的值。你看过例如en.wikipedia.org/wiki/Single_precision,它向你展示了一个浮点的结构?另外,您的意思是“向零舍入”还是“向-无穷大舍入”?
  • 看起来像一本糟糕的书,最好教有用的信息。通常你只使用一个函数,当你不需要的时候不要做一些摆弄。
  • 谢谢 Oli,但我已经看过那个页面了。我了解浮点数的结构,包括有符号位、指数和 mantessa。我不知道如何将位归零。

标签: c floating-point bit-manipulation


【解决方案1】:

因为它只是为了好玩,而且我不确定限制是什么,所以这里有一个变体适用于负数:

float myRoundDown_1 (float v) {        //only works right for positive numbers
    return ((v-0.5f)+(1<<23)) - (1<<23);
}

float myRoundDown_2 (float v) {        //works for all numbers
    static union {
        unsigned long i;
        float f;
    } myfloat;
    unsigned long n;
    myfloat.f = v;
    n = myfloat.i & 0x80000000;
    myfloat.i &= 0x7fffffff;
    myfloat.f = myRoundDown_1(myfloat.f+(n>>31));
    myfloat.i |= n;
    return myfloat.f;
}

【讨论】:

    【解决方案2】:

    float roundDown(float f); 应该是float roundDown(float f) {

    unsigned int notRounded = *(unsigned int *)&amp;f; 与现代编译器优化不兼容。查找“strict aliasing”。

    这是一个向下取整到 2 次方的工作函数:

    #include <stdio.h>
    #include <assert.h>
    #include <string.h>
    
    float roundDown(float f) {
      unsigned int notRounded;
      assert(sizeof(int) == sizeof(float));
      memcpy(&notRounded, &f, sizeof(int));
    
      // zero out the significand (mantissa):
      unsigned int rounded = notRounded & 0xFF800000; 
    
      float r;
      memcpy(&r, &rounded, sizeof(int));
      return r;
    }
    
    int main()
    {
      printf("%f %f\n", 1.33, roundDown(1.33));
      printf("%f %f\n", 3.0, roundDown(3.0));
    }
    

    这应该产生:

    1.330000 1.000000
    3.000000 2.000000
    

    【讨论】:

    • 为什么我们从分数中取出 4 位? (& 0xFFF8 , 前 9 是符号和指数)
    • @dexter4712345 有一个错字(现已修复)。它仍然有效,因为我计算了零,而不是零(零的数量是正确的;我写了0xFFF800000。计算是用 64 位整数完成的,因为这个常数不能在 32 位上表示,但它有效)。
    • 谢谢,这很有帮助。我现在看到主要操作是按位 &。如何修改以处理负数?例如,如何将 -1.5 舍入为 -2?
    • @user2057841 将负数向下舍入到 -inf 的 2 次方确实更复杂。您必须为已经具有 0 有效位的数字(返回相同的数字而不更改)进行特殊情况,对于其他数字,将指数加一。我认为,在使用+ 0x00800000 操作整数表示时,可以在指数上加一。
    • @Pascal notRounded & 0xFF800000 ,是的,这更有意义。
    猜你喜欢
    • 1970-01-01
    • 2012-02-05
    • 2018-09-01
    • 2022-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-23
    相关资源
    最近更新 更多