Kalkhouri
Kalkhouri

Reputation: 341

Splitting a float to two simple integers?

I'm trying to implement a c function that does the following:

input:

float f= 8.947563;

output:

uint32_t o1 = 8;
uint32_t o2 = 947563;

I want to store each of these outcomes in a specific register.

I've found some solutions like modf function but it doesn't really solve my problem because it's o2 will be 0.947563 instead of 947563. Additionally, the number of digits after the fraction can differ according to the user input so I can't simply set a rule to multiply 0.947563 by 1000000 for example because it won't work for numbers with bigger fractions like 8.947556333.

Any idea how to solve this issue? Thanks.

Upvotes: 0

Views: 5237

Answers (4)

old_timer
old_timer

Reputation: 71526

Let's look at some floating point examples:

1.5
0x3FC00000
0 01111111 10000000000000000000000
1.10000000000000000000000<<0
1.10000000000000000000000

1.8
0x3FE66666
0 01111111 11001100110011001100110
1.10000000000000000000000<<0
1.10000000000000000000000

2.1
0x40066666
0 10000000 00001100110011001100110
1.00001100110011001100110<<1
10.0001100110011001100110

12.3456
0x41458794
0 10000010 10001011000011110010100
1.10001011000011110010100<<3
1100.01011000011110010100

So what is it you want to end up with?

12.3456 1100.01011000011110010100 Your two uint32_ts are 0b1100 and 0b01011000011110010100, the former is in a form you desire but the latter? Your example is in decimal which of course has nothing to do with floating point numbers on computers.

Your number 8.947563

0x410F2938
0 10000010 00011110010100100111000
1.00011110010100100111000<<3
1000.11110010100100111000

f = 8.947563
o1 = 1000
o2 = 00000000000011100111010101101011

but

11110010100100111000
11100111010101101011

are not equal. The former is the fraction, so some manipulation is required.

11110010100100111000/100000000000000000000*11110100001001000000

but

0xF2938 * 0xF4240 

does not fit in a 32 bit so we have to step this up to 64 bit.

0000000000000000000000001110011101010110101100101011111000000000/100000000000000000000

and that is where o2 comes from when you do the base 10 math

1110011101010110101100101011111
11100111010101101011

could have been done 32 bit

0xF293*0xF424
11100111010101100011100010101100

and cut off the top 20 bits you get the 947563 o2

11100111010101100011100010101100
11100111010101101011

So if you always want 6 digits then take the fraction times 1000000 and trim off the right number of bits off the bottom. for single the mantissa starts off as 23 bits, for your number the exponent was 3 so 20 bits were left in the mantissa for the fraction. The above cheats some you would want to adjust these numbers to a known alignment just as you would when doing normal floating point math, for example

0xF2938000*0xF4240000;

gives

1110011101010110101100101011111000000000000000000000000000000000

We chop the top 32 bits off:

11100111010101101011001010111110

then take the top 20 bits:

11100111010101101011 result
11100111010101101011 o2

0xF2938 * 0xF4240 was equally fine 0xF293*0xF424 could have given a different result than the desired.

If that's what you want though, 6 digits in decimal then just do this:

   double x, y, z;
   uint32_t a,b;

   x = 8.947563;
   y = modf(x, &z);
   a = z;
   b = y*1000000.0;
   printf("%u %u\n",a,b);

which gives:

8 947563

I'll let you sort out negative numbers.

Upvotes: 2

uɐɪ
uɐɪ

Reputation: 2580

As you have now said that the value is seconds with a nanosecond resolution you need to remove the integer part and keep that as o1, then multiply by 1E9, add 0.5 and then take the integer part of that as o2. The adding of 0.5 is to round off to the nearest nanosecond rather than truncating down. Note that the variable f should be a double not a float because a float can only store around 7 significant digits.

#define TEN_POWER_9 ((double)(1e9))    

o1 = int(f) ;
o2 = int(((f - (double)o1) * TEN_POWER_9) + 0.5) ;

Upvotes: 2

Lundin
Lundin

Reputation: 213690

The function modff breaks a float into integral and fractional parts (modf for double). If you need to convert .947563f to the integer 947563, you'll have to do some manner of trick.

One way which is generic, is to convert the float literal to a string and then parse it. Example:

#include <stdio.h>

static int modff_int (const char* value, int* iptr)
{
  int fractional;
  sscanf(value, "%d.%d", &fractional, iptr);
  return fractional;
}

#define FLOAT_TO_STR(f) #f  // simplified version that only works with float literals


int main (void)
{
  int integral;
  const char* str = FLOAT_TO_STR(8.947563f);
  int fractional = modff_int(str, &integral);

  printf("Integral:   %d\n", integral);
  printf("Fractional: %d\n", fractional);
}

This is however significantly slower than to simply multiply the float .947563f by a constant.

Upvotes: 3

sogen24
sogen24

Reputation: 1

Usually I'm not working with c so i don't know if there's a proper way to do what you want. But what if you convert your float into a string and split them at the point and convert both back to an integer or long or what ever you want. I know that sounds not great but I think that could work.

Upvotes: -1

Related Questions