Reputation: 341
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
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
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
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
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