CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

converting from float to int and vice versa

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
s_mack



Joined: 04 Jun 2009
Posts: 107

View user's profile Send private message

converting from float to int and vice versa
PostPosted: Sun Aug 30, 2009 1:50 pm     Reply with quote

I need to convert a float to an hex representation and back again.

Please don't tell me to just typecast Smile

I need to know what is going on in a device. Internally it is working with floating points but it can only communicate with me via CANBUS in 8 byte hex strings. I need to know what its floats are at any given moment without losing (too much) accuracy.

I see in the CCS help file there is a page that reads:
Quote:
What is the format of floating point numbers?

--------------------------------------------------------------------------------



CCS uses the same format Microchip uses in the 14000 calibration constants. PCW users have a utility Numeric Converter that will provide easy conversion to/from decimal, hex and float in a small window in the Windows IDE. See EX_FLOAT.C for a good example of using floats or float types variables. The format is as follows:


It shows a diagram and some examples (search the ccs c compiler help for "what is the format of floating point numbers?" to see the diagrams).

But I can't figure the math behind what it is doing. Is there a simple way of converting a PIC18 float into int8s and back again?

Thanks.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Aug 30, 2009 2:08 pm     Reply with quote

You don't need to start a new thread on every new float question. Just
add them to your existing thread.

Read this thread and download the floatconv utility that it mentions.
https://www.ccsinfo.com/forum/viewtopic.php?p=63690
s_mack



Joined: 04 Jun 2009
Posts: 107

View user's profile Send private message

PostPosted: Sun Aug 30, 2009 2:17 pm     Reply with quote

Thanks.


And the moment phpBB can come up with a search that gives reasonable results is the moment people will stop asking repeat questions.
s_mack



Joined: 04 Jun 2009
Posts: 107

View user's profile Send private message

PostPosted: Sun Aug 30, 2009 5:32 pm     Reply with quote

That tool did exactly what I asked for, but with so much "magic" that it didn't really help (except to check my work below).

So I started with this theory based explanation: http://www.cs.cornell.edu/~tomf/notes/cps104/floating.html#hex2dec

And I whipped up this messy function:

Code:
int32 float2hex(float value)
{
   int32 bigHex;
   int32 leftH, exponent;
   int bitcount;
   bool zeroOrOne;
   float remainder;
   signed int8 remainderPos;
   if (value < 0)
   {
      bit_set(bigHex, 23);   //sets the sign bit
      value *= -1;
   }
   leftH = (int32)value;   //gets the value to the left of the decimal
   bitcount = 32;
   while ( bitcount > 0 )
   {
      bitcount--;
      if ( bit_test( leftH, bitcount ) )
      {
         exponent = bitcount;
         bitcount = 0;
      }
   }
   exponent += 0x7F;   //adjust for bias
   exponent = (exponent << 0x18);
   bigHex ^= exponent;
   remainder = (value - leftH)*2;   //get the remainder now because we're about to rape leftH
   zeroOrOne = bit_test( leftH, 0 );
   //shift leftH into bigHex until its occupying the most significant bytes
   bitcount = 24;
   while ( bitcount > 0 )
   {
      bitcount--;
      if ( !bit_test( leftH, 23 ) )
      {
         leftH <<= 0x01;
      }
      else
      {
         bit_clear( leftH, 23 );   //clear the implied bit for the mantissa
         bitcount = 0;
      }
   }
   bigHex ^= leftH;

   //figure out which byte we continue with

   while ( bitcount < 24 )
   {
      if ( bit_test ( bigHex, bitcount ) )
      {
         if ( zeroOrOne )
         {
            remainderPos = bitcount - 1;
         }
         else
         {
            remainderPos = bitcount - 2;
         }
         bitcount = 24;
      }
      else
      {
         bitcount++;
      }
   }
   if ( remainderPos < 0 ) remainderPos = 0;

   while ( remainderPos >= 0 )
   {
      if ( (int)remainder ) bit_set( bigHex, remainderPos );
      remainderPos--;
      if ( remainder >= 1 ) remainder -= 1;
      remainder *= 2;
   }

   return bigHex;
}


To my amazement, it actually works for the most part. What I can't figure out is why it won't work with a value of < 3?

3 evaluates correctly (0x80400000)
3.01 does too (0x8040A3D6)
as does everything higher that I've tested.... but 2.99 and below seems to quasi-truncate (2.99 evaluates to 0x80000001 instead of 0x803F5C28)

Any glaring errors I made?

Thanks.
s_mack



Joined: 04 Jun 2009
Posts: 107

View user's profile Send private message

PostPosted: Sun Aug 30, 2009 5:35 pm     Reply with quote

Another oddity... really large numbers don't seem to work right (assuming the tool linked above is correct - I haven't checked manually)


10M works ok, but 100000005678 (for example) does not. It resolves to 0x8D6DFC00 instead of 0xA33A43B6

However for my purposes I'm not concerned with large numbers. The small ones are going to be a problem though.

I'm taking a break. Head hurts.
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Sun Aug 30, 2009 11:50 pm     Reply with quote

Quote:
10M works ok, but 100000005678 (for example) does not.

Without looking too deep in the code details, it's obvious that this can't work
for large numbers beyond the int32 range:
Code:
leftH = (int32)value;   //gets the value to the left of the decimal

But, apart from giving insights in the presentation of float numbers, what's the purpose of this code? CCS C (as nearly all C compilers) is using the standard IEEE float format internally. It can be printed out and read in e.g. by simple union constructs. It's a standard for presentation of float data also in many common file formats.
Code:
union {
float f;
int32 i32;
} f_i;
f_i.f = floatvalue;
bigHex = f_i.i32;
s_mack



Joined: 04 Jun 2009
Posts: 107

View user's profile Send private message

PostPosted: Mon Aug 31, 2009 7:49 am     Reply with quote

The "purpose" is stated in the first post.

I think you're wrong about it using the standard IEEE float format. According to CCS's own help file, it is different (sign bit is in a different local) however that hardly makes a difference really.

I'm unfamiliar with the use of union and the helpfile, while it mentions its existence, doesn't really explain it. From your brief note, I still don't understand how it achieves my goal. I'll have to do some Googling, thanks.

You're right about the large number being out of int32 range and yeah, that's obvious Embarassed int32 is so huge in the scope of what I'm doing that it didn't occur to me that I was using examples large enough to be out of range. As I said though, it doesn't really matter. I'm not using values that high in practice. I'm more concerned as to why the < 3 numbers don't work!
s_mack



Joined: 04 Jun 2009
Posts: 107

View user's profile Send private message

PostPosted: Mon Aug 31, 2009 9:06 am     Reply with quote

Ok, now I've read some and I just need help understanding the union implementation.

First, thank you! That is a WHOLE lot easier and likely a lot more reliable than what I was doing.

HOWEVER... it seems to invert the hex value.

Using the floatconv tool or just manually calculating (or just look at the CCS help file) we know that a float of value 123.45 is represented hexidecimally as 0x 85 76 E6 66.

Using the union example you provided:
Code:
union {
float f;
int32 i32;
} f_i;
f_i.f = 123.45;
bigHex = f_i.i32;


we actually get the reverse (sort of) representation of 0x 66 E6 76 85

Any thoughts on why?

(incidentally, the IEEE representation would be 0x 87 24 8F BE so you can see it is different)
Ttelmah
Guest







PostPosted: Mon Aug 31, 2009 9:41 am     Reply with quote

At the very head of the thread, you have the comment that CCS uses the Microchip 14000 format. It actually saves a tiny amount of time, relative to the IEEE format, on the PIC hardware, hence 'why'.
CCS, actually switches to using the IEEE format, on the very latest larger chips (DSPIC etc.), hence you need to be very careful about assuming what format it is using...

On the byte order, when you print a number, the LSB, is to the right.

Remember you can use a union to a byte array as well:
Code:


union {
float f;
int32 i32;
int8 b[4];
} f_i;



Then you can access the bytes f_i.b[0] to f_i.b[3] in whatever order you want....

Best Wishes
s_mack



Joined: 04 Jun 2009
Posts: 107

View user's profile Send private message

PostPosted: Mon Aug 31, 2009 10:04 am     Reply with quote

Thanks. I wasn't assuming any format and in fact, until this exercise I've never given any thought to this subject at all. I got the format from the help file, then confirmed it manually and with the floatconv tool linked above.

I'm still not sure why using a union stores/displays the representation in the opposite order though. The LSB would be at the right side if it printed the way it was stored.
According to the help file, 123.45 is represented as:

85 76 E6 66, where:

85 is the 8 bit exponent with bias
76 represents the sign bit at the front and the MSB of the mantissa with the first bit being implied 1
E6 are the next 8 bits of the mantissa
66 finally represents the LSB.

So when using union presents it "backwards" it instead implies... well, I'm not sure? By your explanation the exponent byte is now the LSB? And its of course only backwards in 8-bit blocks. Looking at it as a whole it is all messed up.


Anyway, none of it really matters. As long as I know I have to reverse the 4 blocks prior to decyphering the data its all good.
Ttelmah
Guest







PostPosted: Mon Aug 31, 2009 10:18 am     Reply with quote

If you look at the help, under 'what is the format of floating point numbers', it shows the format as:

85 76 E6 66 (for the 123.45) example, with the left hand column, labelled 'lowest byte in RAM'.
'66' is _not_ the LSB, but the MSB.
It is a normal 'standard', in computing, when displaying tables of values in memory, to have the LSB to the left.
If you are encyphering/deciphering the data though, why not just work directly with the bytes, which then involves no 'reversal'. b[0], will be the '85' value.

Best Wishes
s_mack



Joined: 04 Jun 2009
Posts: 107

View user's profile Send private message

PostPosted: Mon Aug 31, 2009 10:21 am     Reply with quote

But you just said LSB to the right!

Shocked

Anyhoo... yes, your suggestion to call out the bytes directly is probably the way to go to avoid confusion.
Ttelmah
Guest







PostPosted: Mon Aug 31, 2009 1:24 pm     Reply with quote

s_mack wrote:
But you just said LSB to the right!

Shocked

Anyhoo... yes, your suggestion to call out the bytes directly is probably the way to go to avoid confusion.


Yes, when you print a number.....

Best Wishes
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group