problems calculation large doubles in MSP430

hi all!

I'm working on a program written on ANSI C for an emmbedded system.... to be precise I am working on an MSP430 with a small webserver that will receive some data into it's RAM through an external hardware and some interruptions. What

I have to do is to get this data, do some calculations and display it through the web server. My problem is, MSP430 compilator (I use IAR emmbedded workbench) limits long doubles to 4 bytes, so I see myself with problems when doing calculations under C with very large numbers. Do you know a way arround it like a library to extend double size or a way to make the compiler meet the standards ANSI C has for doubles? or maybe I have to do math calculation to split the numbers and then rejoin them?

Cheers...

Reply to
Yodai
Loading thread data ...

You're expecting slightly more from ANSI standard C than you should. While 4 bytes isn't quite enough for ANSI C compliant long double (because of the requirement LDBL_DIG >= 10 and LDBL_EPSILON

Reply to
Hans-Bernhard Broeker

There is no such thing as a "minor" violation of a standard. You conform, or don't.

pete

--
pete@fenelon.com "there's no room for enigmas in built-up areas."
Reply to
Pete Fenelon

How large is "very large?"

A bignum library like GNU MP might solve your problem, if it's not too heavyweight and the licensing fits. You'd probably have to pare any such library down in any case.

Otherwise, without knowing more about your application, I guess you'll just have to be careful. Especially with addition and subtraction.

Regards,

-=Dave

--
Change is inevitable, progress is not.
Reply to
Dave Hansen

with

make

Humm...... ok... I give you a sample....

I get a variable that consists of this number :

0x00201582

, but I get it in two "chunks". The reason is not important but, just so you know, I have to make a "volatile int" to a pointer that can only be 2 bytes (compiler limitation) So what I do, I get first 2 bytes the the other 2 as:

varHI = 0x0020 varLO = 0x1582

and I have to put them toguether to multiply them by 'factor' (which value is 0x174b)

factor = 0x174b

If you get a good hexacalculator (windows calc does not do the job since it doesn't accept 40 bit values) the final result of this calculation is

result = 2EB54FB16

which I have to translate to a decimal result as 1253.8.....

let's see a clean sample of how i'd write it:

double Myfunction(void) { volatile int varLO = *external16bitinfoLO ; volatile int varHI = *external16bitinfoHI; volatile int factor = *FACTOR ; double finalresult; //remember, it's 4 byte double

finalresult = (((varHI*0x10000)+varLO)*(factor)/1000000.0f);

return(finalresult);

}

When I start calculating and I multiply the values, the container for this operation is too small (a double that can only get 32bits, long double is not accepted by my compiler), thus not finishing the calculation and forgeting one of the digits of the final result, the 1st one or the last one (meaning 2EB54FB1 without the last 6 or the oposite EB54FB16 without the first 2). That is EXACTLY where my problem is rooted. I need a way to carry on this simple calculations so that I can use the 4.5 bytes of the result for my conversion to decimal (which after deleting all non necessary numbers at the mantissa will be less than 32 bits)

So I see that, since my decimal result is shorter later on, there should be some way to reduce the hexadecimal values, but how?

Thank's for your help, as you can see I am a bit new at this....

Yodai

Reply to
Yodai
[... Big Numbers ...]]

OK so far.

I presume that's by simple division by 1e7, so the final result is

1253.8149654. The question becomes, "How many places beyond the decimal do you really need?" Consider the following simple-minded use of a 4-byte float in gcc:

--- begin included file ---

C:\Dave>type float.c #include

int main(void) { unsigned short var_hi = 0x20; unsigned short var_lo = 0x1582; unsigned int factor = 0x174b; float f = (float)var_hi;

f *= 0x10000; f += var_lo; f *= factor; f /= 1e7f;

printf("The result is %15.8f\n",f); return 0; }

C:\Dave>gcc -ansi -pedantic -Wall float.c

C:\Dave>a The result is 1253.81494141

C:\Dave>

--- end included file ---

That's good to 4 decimal places without trying. If you need all seven, it's going to be trickier.

HTH, -=Dave -=Dave

--
Change is inevitable, progress is not.
Reply to
Dave Hansen
[snip]

get

it

I think I didn't express myself properly... it's not exactly as you say..... the decimal representation of 2EB54FB16 is 12538149654 wich I afterwards divide by 10000000 (decimal) and avoid all decimal positions except the first, since I only need 1253.8

[snip again ]

so... what do you think? any idea?

Cheers...

Yodai

Reply to
Yodai

since

say.....

If you need fixed-point calculations, prepare your factors so that the divisor will be a power of 2 instead of a power of 10 (10^7 here). You can then calculate the number in pieces (several longs) and combine after round-off. The conversion to decimal can be performed last.

If you insist in calculating in decimal, do it from the start: store your numbers in digit arrays and use the school longhand arithmetic on them (slower than the pure binary arithmetic).

HTH

Tauno Voipio tauno voipio @ iki fi

Reply to
Tauno Voipio

it

one

carry

numbers

be

Yes, it is clear that you're a bit new at this. You have done two things wrong here - first, you are not converting your integers to double in the right places, and secondly you are not thinking about the problem like an embedded programmer.

The key line is: finalresult = (((varHI*0x10000)+varLO)*(factor)/1000000.0f);

What this tells the C compiler is to take the 16-bit int "varHi" and multiply it by the long int constant "0x10000". This will automatically protomote "varHi" to a 32-bit long. Then it multiplies by the 16-bit int "factor" - again, this will be automatically promoted to 32-bit long to match the other side of the "*" operator. The resulting numerator is still a 32-bit long. This is then to be divided by a float, so it must first be promoted to a float, and the division is then carried out. In other words, the key part of your calculation, the one that overflows, is done in 32-bit integer arithmetic.

If you simply change all your variables to doubles, you'd have no problems: double varHI = *(volatile int*) external16bitinfoHI;

A 32-bit double is perfectly capable of dealing with these numbers - it will lose a few bits off the end, but these would be chopped in the rounding anyway, and you only have about 24 bits of precision comming in in the first place.

Of course, that's a totally daft way to deal with your calculation. Think about it again - you have a 32-bit value comming in, you need to multiply it by a 16-bit value, then divide by 1 000 000. The result is an integer, scaled by 10. There is no need for floating point anywhere - on a small microcontroller, you should strive to avoid floating point and use integer arithmetic where possible. The answer is to split the 1000000 into bits, and divide in such a way as to avoid overflow while keeping as much precision as possible. Depending on the possible ranges of the data, you may need a 32-bit long for the result rather than a 16-bit int.

int MyFunction(void) { int varHi = *(volatile int*) external16bitinfoHI; unsigned int varLo = *(volatile unsigned int*) external16bitinfoLo; // Must be unsigned !! long int var = ((long) varHi

Reply to
David Brown

Simply change the 15.8 in Dave's printf format to 15.1. I think you need to do some reading up on printf format specifiers.

--
Chuck F (cbfalconer@yahoo.com) (cbfalconer@worldnet.att.net)
   Available for consulting/temporary embedded and systems.
 Click to see the full signature
Reply to
CBFalconer

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.