0

I am trying to round down a float using bit operations in C. I start by converting the float to an unsigned int. I think my strategy should be to get the exponent, and then zero out the bits after that, but I'm not sure how to code that. This is what I have so far:

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. 
Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
user2057841
  • 213
  • 1
  • 4
  • 14
  • floorf(), roundf() and related functions do this. Is this homework? It does not seem overly practical to me. – jim mcnamara Feb 09 '13 at 23:24
  • No, not homework, I am trying to learn bitwise operations in C and this is an exercise in a textbook. I have been struggling with it for several hours, it would be nice if I could get it working. – user2057841 Feb 09 '13 at 23:31
  • 1
    You may need to zero out all, some or none of the mantissa bits, depending on the value of the exponent. Have you looked at e.g. http://en.wikipedia.org/wiki/Single_precision, which shows you the structure of a float? Also, do you mean "round toward zero", or "round towards -infinity"? – Oliver Charlesworth Feb 09 '13 at 23:35
  • 1
    seems like a lousy book, better to teach useful info. normally you just use a function and do not do bit fiddling when you don´t have to. – AndersK Feb 09 '13 at 23:38
  • Thanks Oli, but I have seen that page. I understand the structure of a float, consisting of the signed bit, the exponent, and the mantessa. I don't know how to zero out bits. – user2057841 Feb 09 '13 at 23:40
  • I think it's round toward 0. – user2057841 Feb 09 '13 at 23:41

2 Answers2

1

Since its just for fun, and I'm not sure what the constraints are, here's a variant that DOES work for negative numbers:

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;
}
sree
  • 183
  • 1
  • 5
0

float roundDown(float f); should be float roundDown(float f) {.

unsigned int notRounded = *(unsigned int *)&f; is incompatible with modern compiler optimizations. Look up “strict aliasing”.

Here is a working function to round down to the power of two:

#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));
}

This should produce :

1.330000 1.000000
3.000000 2.000000
Community
  • 1
  • 1
Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • 1
    Why do we take 4 bits from the fraction? (& 0xFFF8 , first 9 is sign and exponent) –  Feb 09 '13 at 23:50
  • 1
    @dexter4712345 There was a typo (now fixed). It was still working because I had counted the zeroes, not the ones (there was the correct number of zeroes; I had written `0xFFF800000`. The computation was done with 64-bit integers because this constant could not be represented on 32-bit, but it worked). – Pascal Cuoq Feb 09 '13 at 23:54
  • Thanks, this has been very helpful. I see now that the main operation is the bitwise &. How can I modify to deal with negative numbers? For example, how can I round -1.5 down to -2? – user2057841 Feb 09 '13 at 23:55
  • @user2057841 Rounding negative numbers down to the power of two towards -inf is really more complicated. You have to make a special case for numbers that already have a 0 significand (return the same number without change), and for the others, add one to the exponent. Adding one to the exponent can be done while manipulating the integer representation with `+ 0x00800000`, I think. – Pascal Cuoq Feb 09 '13 at 23:59
  • @Pascal notRounded & 0xFF800000 , yeah that makes much more sense. –  Feb 10 '13 at 00:03
  • How can I check if the number has a 0 significand? Would 1< – user2057841 Feb 10 '13 at 00:05
  • @user2057841 The significand is zero if `(notRounded & 0x007FFFFF) == 0` – Pascal Cuoq Feb 10 '13 at 00:07
  • Hi thanks again for your help. I am still trying to get it working for negative numbers and such, to make sure I understand. I am trying to zero out the bottom digits from the unsigned short. Since I need to zero out a different number of bits everytime, how can I do this? I need to zero out the bottom 23-exponent bits, but how can I make a mask that will work for a different exponent each time? – user2057841 Feb 10 '13 at 06:24