tags:

views:

217

answers:

8

My brain farts a lot when it comes to hex. Some people are abidextrous and others are just plain right handed... well, kind of like that, I'm VERY base10 I guess.

Anyway... I'm trying to make some firmware more efficient. We have a function that's calculating a vehicle's speed based on some hex data it gets via the CANBUS. Right now I'm converting it to a float so I can wrap my head around it but I'm wondering if we'd use less ROM space if we left it in integer format? Can that be done without losing accuracy? Right now my floats are accurate to 1/16th of a kph. I know this function seems simple, but running hundreds of times per second it is bogging it down a little.

First, here is some sample data:

[06] [3c] ... [06] [3a] ... [06] [3b] ... [06] [46] ... [06] [3b] ...

I've left off the other 6 bytes as they don't relate to speed. The byte on the left we'll call speed_a and the byte on the right is speed_b. Here is the function to convert:

float calculateSpeed()
{
    float speed;
    speed = ( ( float )speed_a * 256.0 + speed_b ) / 16.0;
    return speed;
}

So the data above would translate to:

99.7500 99.6250 99.6875 100.3750 99.6875

and that does accurately reflect the true speed of the vehicle in kph. For our application though, we don't really care what the true speed is because everything is relative. As long as we don't lose resolution we're happy. I thought about just keeping everything in INT form, but then when you divide by 16 it just truncates.

I'm not an idiot about most things... but I'm an idiot with base2.

Lil' help? Thanks.

+1  A: 

You will save space for a given precision if you use fixed point math given the requirements you describe, because you can use just the number of bits you need in order to store the fractional portion.

However, most compilers store a float as a 32 bit value and most fixed-point math libraries I have seen also use 32 bits as storage. Unless you can represent the number ranges you need (whole and fractional part) using a 16-bit fixed-point number, there will be no storage difference between float and (32-bit) int.

Bottom Line Unless you want to deal with 16-bit fixed-point numbers, int and float probably use the same amount of storage on your system.

Eric J.
+8  A: 

Simply don't divide by 16.0. You can still compare, sort, add or subtract speeds.

Mehrdad Afshari
Right. This technique is comparable to doing financial calculations in cents only (instead of fractional dollars.cents).
Greg Hewgill
Right after posting the Q and going for dinner it occured to me that it might be as simple as this. Thank you.
Steven
+1  A: 

You can go with:

int calculateSpeed () {
    int speed;
    speed = ( (speed_a * 256 ) + speed_b );    
    return speed;
}

I just basically dropped the div part, since you dont need it: int is at least 32bits - while you have 2 bytes that are 16bits together.

rmn
+2  A: 

Integers are always faster than floats. Instead of using floats to represent kph, use integers to represent 1/16ths of a kph.

Then your code becomes:

int calculateSpeed() {
    return speed_a * 256 + speed_b
}

Only at the point where you need to display the value to a user would I convert back to a float:

int s = calculateSpeed();
printf ("Speed = %f\n", ((float)(s))/16.0);

Depending on how smart your compiler is, and how good your CPU is (I could be wrong, but this sounds like an embedded system), the line:

    return (speed_a << 8) + speed_b

may be faster.

paxdiablo
You are correct - it is embedded. And there's no need (or even ability) to display the value. And I think you're right about shifting vs. multiplication... in the compiler's help file it hints that is the prefered way to go. Thanks.
Steven
A: 

Steven,

Integer division simply gives an integer result. Real (floating point) division gives a floating point result.

In your case you could store this as meters, or centimeters or even microns per second. Then when you do integer division of the appropriate scale you won't care about the truncation and you can defer the conversion into Kph until it's time to print/display results (or export to anything that's expecting those units, etc).

Incidentally another old approach is to perform pairs of (integer) division and modulus operations ... storing results as quotient/remainder pairs until it's time to print/display/export. There have been whole libraries of mathematics routines implemented with this sort of "scaled integer arithmetic" model ... which can offer significant performance and precision advantages over floating point calculations.

Jim Dennis
+2  A: 

You can use a scale of 0.01. The integer 9969 then represents 99.69 kph. This will avoid the base 2 complications.

0.01 is six times better than the resolution of 0.0666. Two times better would have been sufficent. You can then use 2 bytes per measurement and represent up to 327 kph.

If the division by 100 is needed (e.g. for output) then there are ways for approximate, but fast division by a constant (100 in case).

Peter Mortensen
Except I can't change the data that's being generated... that's what I have to work with. So while .01 would be nice, that's not the cards that are dealt.
Steven
A: 

Is this allowed to lose data? If it isn't you can't do much about this decimals... Otherwise:

int speed = ((float_a << 8) | float_b) >> 4;

Make the two char a short and drop the last 4 bits (which is the same as dividing it by 16 and getting the integer part).

You also have a performance gain in that sinse using bitwise operations is a little bit faster than using multiplication / division (despite this gain is almost insignificant for actual computers).

Havenard
A: 

If you only want to convert to decimal numbers for display, without using floating point arithmetic, you could do something like this if speed is measured in sixteenths of a km/h:

printf("%d.%04d", speed / 16, 10000 * (speed % 16) / 16);

In an embedded environment, you may certainly save a lot of ROM space if you don't need to link in any floating point arithmetic libraries. It sounds like it might be a good idea for you to eliminate all use of floating point in your application. (Not to mention also avoiding the use of printf(), but that's another story.)

If your measurement equipment provides no more precision than sixteenths, there is no point in showing more than two decimal places. Modification of the above example to account for that format is left as an exercise for the reader.

Greg Hewgill