EmbeddedRelated.com
Forums

C18 calculation question - novice...

Started by Dennis August 3, 2011
On 04/08/11 16:05, John Devereux wrote:
> David Brown<david.brown@removethis.hesbynett.no> writes: > >> On 04/08/11 08:23, John Devereux wrote: >>> Tim<tim@seemywebsite.please> writes: >>> >>>> On Wed, 03 Aug 2011 08:08:12 +0100, John Devereux wrote: >>>> >>>>> Dennis<user@example.net> writes: >>>>> >>> >>> [...] >>> >>> >>>>>> >>>>>> volatile unsigned int S_RH; //raw serial rh value, 16 bits >>>> volatile >>>>>> unsigned long RH; //RH value, 32 bits >>>>>> >>>>>> #pragma code // declare executable instructions void main (void) >>>>>> { >>>>>> >>>>>> //load test serial data >>>>>> S_RH = 25424; >>>>>> >>>>>> RH = 125*(S_RH); //65536; >>>>> >>>>> This only converts to a long *after* the 125xS_RH has been performed. So >>>>> it is overflowing before being assigned to RH. Try >>>>> >>>>> RH = 125L * S_RH; >>>>> >>>>> The L specifies a "long literal" value, this forces the entire >>>>> calculation to be done in "long". >>>>> >>>>> Other ways would be: >>>>> >>>>> RH = 125 * (long)S_RH; >>>>> >>>>> or >>>>> >>>>> RH = S_RH; >>>>> RH *= 125; >>>>> >>>>>> Even with the calculation reduced to 125 x S_RH to limit the size of >>>>>> the result I can see that the result gets clipped with RH register >>>>>> containing 0x00007E10 after the above calculation. The correct value >>>>>> which would be 0X00307E10. So it appears the RH is being treated as a >>>>>> 16 bit variable. >>>>> >>>>> It is S_RH and the value 125 that are 16 bit. You need to "coerce" one >>>>> of them to 32 bit in order to do 32 bit math. >>>> >>>> I have run into aggressively optimizing compilers that need to have >>>> _both_ arguments cast to long to reliably do the calculation right, in >>>> spite of the C standard. >>>> >>>> Saying 125L * (long)(S_RH) only uses up a few more characters, doesn't >>>> change what a correct compiler will do, and saves your code from the edge >>>> cases where a compiler _doesn't_ do its job. >>> >> >> I would also say that "125 * (long)(S_RH)" is clearer, and therefore a >> better choice. Even better, however, is "125 * (uint32_t)(S_RH)", >> since that makes it entirely clear that you want 32-bit arithmetic >> here. > > not > > RH = (uint32_t)((uint32_t)125 * (uint32_t)(S_RH)) > > ...just to be really sure? :) >
No - you want enough to be clear to the reader, and to the compiler, without drowning the code. (I know you used a smiley, but some people really write code like that.)
>> The use of "int", "long", etc., should be deprecated in most >> embedded code. > > I know I'm supposed to do it, but I find uint32_t et al ugly. I did have > a phase of using them, I think I ran into trouble when my code had to > interface to newlib. Can't remember now, anyway I went back to shorts, > ints and longs as god intended - and the rules of C. My rationale is > that it is only things like hardware accesses that require precise > widths, and this code will be inherently non-portable anyway (we are > talking microcontroller contexts here). >
No, there are lots of things that need precise sizes. Most of the time you are happy with "big enough", but you sometimes want to avoid "too big". C doesn't have support for ranged types (like Pascal, Ada, etc.) where you can say what you actually want (helping the writer and other readers, and helping the compiler spot mistakes) - sized types are the nearest it has. It is in fact in portable code that sized types are most important. Classic cases are code written for 32-bit systems with the assumption that "int" is 32-bit, and then using the same code on 8-bit or 16-bit systems with 16-bit ints. You can well argue that this is a case of making unwarranted assumptions - after all, if you want to be sure of 32-bit with standard C types, you must use "long int" and not "int". But people make such assumptions - being explicit with your types avoids that. It also avoids problems with the irritating semi-C compilers that have things like 16-bit "long ints". Some of this is a matter of opinion and style. You may think "uint32_t" is ugly, but personally I hate it when people use "short" or "long" as a type. These are /adjectives/, not types. The types are "short int" and "long int" respectively. It's almost as bad as illiterate code authors who use "char" when they want a number. "char" is short for "character" - a letter, such as an element in a string. If you want a small number, use "int8_t" or "uint8_t" - that tells people exactly what you are talking about. IMHO, of course.
>> Compilers (and their libraries) often have faster code for unsigned >> multiplication or division, so it is worth making sure you use >> unsigned explicitly unless you really need signed arithmetic. >> >> It is conceivable that a compiler will generate better code in this >> case - it may note that "125" fits in 8 bits, and make a call to an >> 8x32 bit multiply routine. Sometimes compilers generate different >> code even when the meaning (according to the C standards) are >> identical. >> > > [...] > >
On 04/08/11 19:21, John Temples wrote:
> On 2011-08-03, Dennis<user@example.net> wrote: >> I'm trying to interface a SHT21 humidity sensor to a PIC (and learning >> C18 along the way, MPLAB, C18, PIC18LF44K22). > > [...] > >> So the formula will become: >> >> RH = (100 x 125 x S_RH)/2^16 - 600 > > Keep in mind that C18 does not do integer promotions by default, so an > expression like "100 * 125" will overflow. You can enable integer > promotions if you want to have standard C behavior. >
A compiler that does not do standard C integer promotions, or treat "100" and "125" as integers, has no business calling itself a "C compiler".
On 5/08/2011 1:21 AM, John Temples wrote:
> On 2011-08-03, Dennis<user@example.net> wrote: >> I'm trying to interface a SHT21 humidity sensor to a PIC (and learning >> C18 along the way, MPLAB, C18, PIC18LF44K22). > > [...] > >> So the formula will become: >> >> RH = (100 x 125 x S_RH)/2^16 - 600 > > Keep in mind that C18 does not do integer promotions by default, so an > expression like "100 * 125" will overflow. You can enable integer > promotions if you want to have standard C behavior. >
Thanks John.
On 4/08/2011 4:13 AM, Tim wrote:
> On Wed, 03 Aug 2011 14:02:06 +0800, Dennis wrote: > >> I'm trying to interface a SHT21 humidity sensor to a PIC (and learning >> C18 along the way, MPLAB, C18, PIC18LF44K22). >> >> A 16 bit serial value, S_RH, is read from the sensor. To convert this >> value into relative humidity, RH, it needs to be inserted into an >> equation. >> >> RH = (125 x S_RH)/ 2^16 - 6 >> >> The data sheet gives a test value of S_RH = 25424 giving RH = 42.5% >> which works nicely. >> >> >> I want the result in a from that I can easily convert to >> hundreds/tens/units/tenths etc for outputing from the uC. To achieve >> this I want to multiply the above formula by 100 so the result >> represents tens/units/tenths/hundredths with the decimal point shifted >> right two places. >> >> So the formula will become: >> >> RH = (100 x 125 x S_RH)/2^16 - 600 >> >> >> Now for my problem..... >> >> I'm having problems with what number types to use. >> >> The maximum value for RH DURING the calulation should be; 100 x 125 x >> 65536 = 81987500 which is Ox30D3CF2C >> >> This needs 32 bits so I define RH as unsigned long but it appears to >> overflow corrupting the result when I run the code in the simulator >> >> >> >> >> >> volatile unsigned int S_RH; //raw serial rh value, 16 bits volatile >> unsigned long RH; //RH value, 32 bits >> >> #pragma code // declare executable instructions void main (void) >> { >> >> //load test serial data >> S_RH = 25424; >> >> RH = 125*(S_RH); //65536; >> >> .....} >> >> >> Even with the calculation reduced to 125 x S_RH to limit the size of the >> result I can see that the result gets clipped with RH register >> containing 0x00007E10 after the above calculation. The correct value >> which would be 0X00307E10. So it appears the RH is being treated as a 16 >> bit variable. >> >> Apologies for the long post, any advice gratefully received. > > At one place I worked we had a long drawn out interview question (it > could take an hour to explore all the ramifications). You're already a > lot further along that many. >
After 15 years of labouring with assembly before moving to C I hope so! :)