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

Compiler Bug or Do I Need to Get Myself a Good C Book
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
matthewsachs



Joined: 19 Jun 2016
Posts: 22

View user's profile Send private message

Compiler Bug or Do I Need to Get Myself a Good C Book
PostPosted: Sat Oct 26, 2019 11:56 pm     Reply with quote

PCWH v5.090

I spent hours tracking down this bug.

The original function looked like this:

Code:

int16 ADCtoFLOW(int16 ADCReading, int16 ScaleFactor)
{
   int32 res = 0L;
   
   res = ((int32)(ADCReading)) * ((int32)(1000L));
   if(res <= ((int32)(ScaleFactor))) res = 0L;
   else res = res / ((int32)(ScaleFactor));
   return (int16)res;
}


but it returned garbage all the time. It seems that using a cast of the function's second argument (i.e. (int32)(ScaleFactor)) in 2 places was causing the problem.

Changing the function by adding another temporary variable fixed the issue:

Code:

int16 ADCtoFLOW(int16 ADCReading, int16 ScaleFactor)
{
   int32 res = 0L;
   int32 SFCT = 0L;
   
   SFCT = (int32)ScaleFactor;
   res = ((int32)(ADCReading)) * ((int32)(1000L));
   if(res <= SFCT) res = 0L;
   else res = res / SFCT;
   return (int16)res;   
}


Am I missing something obvious?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 1:26 am     Reply with quote

Post a couple sample numbers for ADCReading and the ScaleFactor
that caused the problem to occur. And tell us what the bad result
was, and what the expected result was.

In other words, allow us to duplicate your problem.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 6:39 am     Reply with quote

Also tell us what chip is involved.

I had an issue a couple of compiler versions ago, with casting not working
correctly with signed valeus on a PIC24. But this should be fixed in .090
matthewsachs



Joined: 19 Jun 2016
Posts: 22

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 7:26 am     Reply with quote

#include <18F66K80.h>
#device adc=12

With the function implemented using 2 casts:

ADCtoFLOW(8, 7371) -> returned 7936 (instead of 1 expected)
ADCtoFLOW(2265, 7371) -> returned 36609 (instead of 307 expected)
ADCtoFLOW(4092, 7371) -> returned 28674 (instead of 555 expected)

Not that it matters, but in the actual code, this function is passed a pair of int16 global variables (not the hard coded values shown above) that a) hold the ADC Reading and b) the ScaleFactor both of which are guaranteed NOT to be changing while this function executes - especially not the "twice cast" ScaleFactor variable which is only set once on power-up.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 7:37 am     Reply with quote

matthewsachs wrote:

ADCtoFLOW(8, 7371) -> returned 7936 (instead of 1 expected)
ADCtoFLOW(2265, 7371) -> returned 36609 (instead of 307 expected)
ADCtoFLOW(4092, 7371) -> returned 28674 (instead of 555 expected)

I ran it with all three sets of parameters and it worked fine.
It gave the results below in MPLAB vs. 8.92 simulator:
Quote:

result = 1
result = 307
result = 555


I used version 5.090:
Code:

CCS PCH C Compiler, Version 5.090, xxxxx     27-Oct-19 06:34

Filename:   C:\Program Files\PICC\Projects\PCH_Test\PCH_Test.lst

ROM used:   772 bytes (1%)
     Largest free fragment is 64764
RAM used:   7 (0%) at main() level
     33 (1%) worst case
Stack used: 0 locations
Stack size: 31



Test program:
Code:

#include <18F66K80.h>
#device adc=12
#use delay(internal=4M)
#use rs232(baud=9600, UART1, ERRORS)

int16 ADCtoFLOW(int16 ADCReading, int16 ScaleFactor)
{
   int32 res = 0L;
   
   res = ((int32)(ADCReading)) * ((int32)(1000L));
   if(res <= ((int32)(ScaleFactor))) res = 0L;
   else res = res / ((int32)(ScaleFactor));
   return (int16)res;
}

//====================================
void main(void)
{
int16 result;

result = ADCtoFLOW(8, 7371);
printf("result = %lu \r", result);

while(TRUE);
}
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 8:18 am     Reply with quote

I'm wondering if we have a similar issue to the one we had just a few
threads ago, with the install having an old DLL somehow (multiple compilers
or something not cleaned away in the install). It was 5.085 that had the
casting issue.
I suggest deleting your compiler, and doing a clean re-install and seeing
if the problem then disappears.
matthewsachs



Joined: 19 Jun 2016
Posts: 22

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 8:22 am     Reply with quote

Thanks PCM.

This is more along the lines how this is used in my application:

Code:
#include <18F66K80.h>
#device adc=12
#use delay(internal=4M)
#use rs232(baud=9600, UART1, ERRORS)

int16   ADCR = 8L;
int16   SF = 7371L;

int16 ADCtoFLOW(int16 ADCReading, int16 ScaleFactor)
{
   int32 res = 0L;
   
   res = ((int32)(ADCReading)) * ((int32)(1000L));
   if(res <= ((int32)(ScaleFactor))) res = 0L;
   else res = res / ((int32)(ScaleFactor));
   return (int16)res;
}

//====================================
void main(void)
{
int16 result;

result = ADCtoFLOW(ADCR, SF);
printf("result = %lu \r", result);

while(TRUE);
}


Should I try to post the generated assembly code I see on my end with the two versions of this function?
matthewsachs



Joined: 19 Jun 2016
Posts: 22

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 8:23 am     Reply with quote

Ttelmah wrote:
I'm wondering if we have a similar issue to the one we had just a few
threads ago, with the install having an old DLL somehow (multiple compilers
or something not cleaned away in the install). It was 5.085 that had the
casting issue.
I suggest deleting your compiler, and doing a clean re-install and seeing
if the problem then disappears.


Very interesting. I will try that right away.
temtronic



Joined: 01 Jul 2010
Posts: 9229
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 11:03 am     Reply with quote

Since you always zero 'res' at the beginning..
you 'might' get tighter code by doing this...

if( ((int32)(ADCReading)) * ((int32)(1000L)) > ((int32)(ScaleFactor))) res = res / ((int32)(ScaleFactor));

I did 'copy/paste' the code so maybe too many brackets.....
matthewsachs



Joined: 19 Jun 2016
Posts: 22

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 11:12 am     Reply with quote

Deleting the old compiler (PICC folder and all) and re-installing a fresh version of 5.090 did NOT solve the problem.

Not a big deal as I have a workaround, but I would love to find out the root cause.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 11:14 am     Reply with quote

I'd suggest:

if( ((ADCReading * 1000LL) > (int32)ScaleFactor) res/ =(int32)ScaleFactor);

It seems wasteful to cast a constant to int32. Just declare it as int32 (LL).
Also on a lot of compilers it is more efficient to use /=, rather than
val=val/
Separately though the optimiser should be ignoring the extra brackets,
having the variable bracketed potentially means this has to be
'evaluated', then 'cast', when it is the variable that just wants to be cast
itself. I think the optimiser will ignore this, but it seems silly to be
relying on this... Smile
It's not actually needed to cast both sides up in a sum. Only one side
needs to be cast, the other is automatically done.
matthewsachs



Joined: 19 Jun 2016
Posts: 22

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 11:21 am     Reply with quote

temtronic wrote:
Since you always zero 'res' at the beginning..
you 'might' get tighter code by doing this...

if( ((int32)(ADCReading)) * ((int32)(1000L)) > ((int32)(ScaleFactor))) res = res / ((int32)(ScaleFactor));

I did 'copy/paste' the code so maybe too many brackets.....


Thanks - your recommendation is a lot cleaner than my if/else

I changed it to this

Code:

res = ((int32)(ADCReading)) * ((int32)(1000L));
   if(res > ((int32)(ScaleFactor))) res = res / ((int32)(ScaleFactor));
   return (int16)res;


But it still returns with incorrect values.

If I store (int32)(ScaleFactor) in another temp variable and then use it twice, all is well.
matthewsachs



Joined: 19 Jun 2016
Posts: 22

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 11:29 am     Reply with quote

Ttelmah wrote:
I'd suggest:

if( ((ADCReading * 1000LL) > (int32)ScaleFactor) res/ =(int32)ScaleFactor);

It seems wasteful to cast a constant to int32. Just declare it as int32 (LL).
Also on a lot of compilers it is more efficient to use /=, rather than
val=val/
Separately though the optimiser should be ignoring the extra brackets,
having the variable bracketed potentially means this has to be
'evaluated', then 'cast', when it is the variable that just wants to be cast
itself. I think the optimiser will ignore this, but it seems silly to be
relying on this... Smile
It's not actually needed to cast both sides up in a sum. Only one side
needs to be cast, the other is automatically done.


Thanks for the tip and the rationale.
I also didn't know that "LL" was an option - this is great info since I am casting constants all over the place :-(

I tried a variation of your suggestion

Code:
int32 res = 0L;
   
   res = ((int32)(ADCReading)) * (1000LL);
   if(res > ((int32)(ScaleFactor))) res /= ((int32)(ScaleFactor));
   return (int16)res;


and it's still returning wrong values.

Yet

Code:

 int32 res = 0LL;
   int32 scl_fctr = 0LL;
   
   scl_fctr = ((int32)ScaleFactor);
   
   res = ((int32)(ADCReading)) * (1000LL);
   if(res > scl_fctr) res /= scl_fctr;
   return (int16)res;


works as expected.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 12:04 pm     Reply with quote

Having to use an intermediate variable, was exactly the problem I was seeing.
What is odd, is I have run up the code, and like PCM_Programmer, it is
working on my system!...
Uuurgh!.
I've tried V5.076, 5.078 (gives the wrong result), 5.080, 5.085 & 5.090.
The only one that gives a wrong result is 5.078.

Show your actual test program. Not just the routine, but the code being used
to call it. I'm wondering if there is something else giving the issue on what
you are doing. Has the sort of 'smell' of a variable being corrupted by
an interrupt handler or some similar oddity.
matthewsachs



Joined: 19 Jun 2016
Posts: 22

View user's profile Send private message

PostPosted: Sun Oct 27, 2019 1:48 pm     Reply with quote

I'll post the code shortly.

I also thought it may be a case of the value getting corrupted, but the wrong values are not randomly (or ever) changing. For every unique ADC value I get the same unique (but wrong) function result no matter if I cycle power or let it run for hours.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
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