compilers are giving different results

Go To Last Post
30 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wrote an array mapper and the ending result is bugged. I tried the code with an online compiler and the code works.  I'm trying to figure out what is wrong. I know there is some over head here but I didn't want to remove anything, although I did simplified it and get the same bugged results. The mapper code is working fine it is just when I'm not using a mapping (i.e 1,2,3,4,5....)  I get any result on the 3rd row showing on the second row.

 

for example

 

reportBuffer[BUTTON_ROW_1]=1;

reportBuffer[BUTTON_ROW_2]=0;

reportBuffer[BUTTON_ROW_3]=0;

 

after the mapper gives me the correct answer.
 

reportBuffer[BUTTON_ROW_1]=1;

reportBuffer[BUTTON_ROW_2]=0;

reportBuffer[BUTTON_ROW_3]=0;

 

or

 

reportBuffer[BUTTON_ROW_1]=0;

reportBuffer[BUTTON_ROW_2]=1;

reportBuffer[BUTTON_ROW_3]=0;

 

after the mapper gives me again the right answer
 

reportBuffer[BUTTON_ROW_1]=0;

reportBuffer[BUTTON_ROW_2]=1;

reportBuffer[BUTTON_ROW_3]=0;

 

but this

 

reportBuffer[BUTTON_ROW_1]=0;

reportBuffer[BUTTON_ROW_2]=0;

reportBuffer[BUTTON_ROW_3]=1;

 

after the mapper gives me
 

reportBuffer[BUTTON_ROW_1]=0;

reportBuffer[BUTTON_ROW_2]=0x80; //huh? why.

reportBuffer[BUTTON_ROW_3]=0;

 

I decided to try it on another compiler and the result is correct in all 3 cases.  Below shows a simple demonstration of  what I mean.

 

 

Here is the online compiler

http://rextester.com/l/c_online_...

I put the following code in here.


#include  <stdio.h>
#define REPORT_ID 0
#define X_MAIN_STICK 4
#define Y_MAIN_STICK 5
#define Z_AXIS_1 6
#define X_SECONDARY_STICK 7
#define Y_SECONDARY_STICK 8
#define Z_AXIS_2 9
#define SLIDER 10
#define DIAL 11
#define HAT 12
#define BUTTON_ROW_1 1
#define BUTTON_ROW_2 2
#define BUTTON_ROW_3 3

unsigned char reportBuffer[24];
unsigned char mapper[24]={1,2,3,4,5, 6, 7, 8, 9,10,11 ,12 ,13 ,14 ,15,16,17,18,19,20,21,22,23,24}; 

int main(void)
{

reportBuffer[BUTTON_ROW_3]=1;//write a 1 to the last array for a test

	char i;
	unsigned long temp;
	char dPadButtons[4]={0,0,0,0};
	char b=0;//button row
	char x=0;//dpad button index
	char tempMap[24];
	memcpy(tempMap, mapper,24);//preservers old config so it can be reused.
	for (i=0;i<8;i++) if ( tempMap[i]   > 100 ) {b=1; dPadButtons[x]=i;x++;tempMap[i]   =0;}
	for (i=0;i<8;i++) if ( tempMap[i+8] > 100 ) {b=2; dPadButtons[x]=i;x++;tempMap[i+8] =0;}
	for (i=0;i<8;i++) if ( tempMap[i+16]> 100 ) {b=3; dPadButtons[x]=i;x++;tempMap[i+16]=0;}

	if (b)//d-pad is in use.
	{
		//udlr 0
		if 		(reportBuffer[b] & 1 << dPadButtons[0]) reportBuffer[HAT] = 0;//u
		else if (reportBuffer[b] & 1 << dPadButtons[1]) reportBuffer[HAT] = 4;//d
		else if (reportBuffer[b] & 1 << dPadButtons[2]) reportBuffer[HAT] = 6;//l
		else if (reportBuffer[b] & 1 << dPadButtons[3]) reportBuffer[HAT] = 2;//r 

		//diags
		if      ( reportBuffer[HAT] == 0 && reportBuffer[b] & 1 << dPadButtons[2]) reportBuffer[HAT] = 7;//ul
		else if ( reportBuffer[HAT] == 4 && reportBuffer[b] & 1 << dPadButtons[3]) reportBuffer[HAT] = 3;//dr
		else if ( reportBuffer[HAT] == 4 && reportBuffer[b] & 1 << dPadButtons[2]) reportBuffer[HAT] = 5;//dl
		else if ( reportBuffer[HAT] == 0 && reportBuffer[b] & 1 << dPadButtons[3]) reportBuffer[HAT] = 1;//ur
	}

	unsigned long button = ( (unsigned long)reportBuffer[BUTTON_ROW_1] | ((unsigned long)reportBuffer[BUTTON_ROW_2] << 8) | ((unsigned long)reportBuffer[BUTTON_ROW_3] << 16) );

	temp=0; for (i=0;i<24;i++) if ( tempMap[i] != 0 ) if (button & 1 << i) temp |= ((unsigned long)1 << tempMap[i]-1 );

	reportBuffer[BUTTON_ROW_3] = (temp>>16);
	reportBuffer[BUTTON_ROW_2] = (temp>>8);
	reportBuffer[BUTTON_ROW_1] = (temp);

printf("1::%i\n",reportBuffer[BUTTON_ROW_1]);
printf("2::%i\n",reportBuffer[BUTTON_ROW_2]);
printf("3::%i\n",reportBuffer[BUTTON_ROW_3]);

    if (reportBuffer[BUTTON_ROW_3]==1) printf("correct, it is a 1"); else printf ("incorrect");

return 0;
}

 

the result here is "correct, it is a 1"

 

now my firmware is compiled on avr studio 6.2 with avr-gcc (AVR_8_bit_GNU_Toolchain_3.4.5_1522) 4.8.1

 

and this code has the same defines and array but is not defined in the same spot.

reportBuffer[BUTTON_ROW_3]=1;//testing with a 1

char i;
unsigned long temp;
char dPadButtons[4]={0,0,0,0};
char b=0;//button row
char x=0;//dpad button index
char tempMap[24];
memcpy(tempMap, mapper,24);//preservers old config so it can be reused.
for (i=0;i<8;i++) if ( tempMap[i]   > 100 ) {b=1; dPadButtons[x]=i;x++;tempMap[i]   =0;}
for (i=0;i<8;i++) if ( tempMap[i+8] > 100 ) {b=2; dPadButtons[x]=i;x++;tempMap[i+8] =0;}
for (i=0;i<8;i++) if ( tempMap[i+16]> 100 ) {b=3; dPadButtons[x]=i;x++;tempMap[i+16]=0;}

if (b)//d-pad is in use.
{
	//udlr 0
	if 		(reportBuffer[b] & 1 << dPadButtons[0]) reportBuffer[HAT] = 0;//u
	else if (reportBuffer[b] & 1 << dPadButtons[1]) reportBuffer[HAT] = 4;//d
	else if (reportBuffer[b] & 1 << dPadButtons[2]) reportBuffer[HAT] = 6;//l
	else if (reportBuffer[b] & 1 << dPadButtons[3]) reportBuffer[HAT] = 2;//r 

	//diags
	if      ( reportBuffer[HAT] == 0 && reportBuffer[b] & 1 << dPadButtons[2]) reportBuffer[HAT] = 7;//ul
	else if ( reportBuffer[HAT] == 4 && reportBuffer[b] & 1 << dPadButtons[3]) reportBuffer[HAT] = 3;//dr
	else if ( reportBuffer[HAT] == 4 && reportBuffer[b] & 1 << dPadButtons[2]) reportBuffer[HAT] = 5;//dl
	else if ( reportBuffer[HAT] == 0 && reportBuffer[b] & 1 << dPadButtons[3]) reportBuffer[HAT] = 1;//ur
}

unsigned long button = ( (unsigned long)reportBuffer[BUTTON_ROW_1] | ((unsigned long)reportBuffer[BUTTON_ROW_2] << 8) | ((unsigned long)reportBuffer[BUTTON_ROW_3] << 16) );

temp=0; for (i=0;i<24;i++) if ( tempMap[i] != 0 ) if (button & 1 << i) temp |= ((unsigned long)1 << tempMap[i]-1 );

//this is busted seems like the issue is in the loop above.
reportBuffer[BUTTON_ROW_3] = (temp>>16);
reportBuffer[BUTTON_ROW_2] = (temp>>8);
reportBuffer[BUTTON_ROW_1] = (temp);

if ( reportBuffer[BUTTON_ROW_3] )= blinkLED(1); else  blinkLED(2); 

 

What am I missing? row 3 should have been a 1.

Last Edited: Thu. Oct 19, 2017 - 02:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
for (i=0;i<24;i++) ... (button & 1 << i) ...

This do not work. "1 << i" is calculated first, and because 1 is a signed int ...

Later in that line you have "(unsigned long)1 <<", so I think you know what the ... is standing for.

 

BTW: Your use of "char" is very bad practice.

Stefan Ernst

Last Edited: Thu. Oct 19, 2017 - 04:01 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In other words, this code will return true on an AVR compiler, but false on x86 or ARM.

 

int main(void)
{
    unsigned long button = 1 << 23;
    return button == 0;
}

 

... but this one will return false in all cases:

int main(void)
{
    unsigned long button = 1UL << 23;
    return button == 0;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ahhh, ok got it thx!

 

Your use of "char" is very bad practice.

 as in not very portable?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

There are three types of "char":

 

char

unsigned char

signed char

 

The latter two are more usually referred to as uin8_t and int8_t. If you are using the 1 byte storage to hold 0..255 then use uint8_t, if you are using it for -128 .. +127 then use int8_t and if you are using it to hold characters like 'h','i' then use char. Don't use char otherwise.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fair enough, I have played with this a bit in effort to minimize code space. I find times when int8_t is better and other times where char is better.  Though in my case I hardly ever need to use char for letters and normally use defines. For code readability it is unfortunate that avr studio does not recognize int8_t. Guessing there is a way to add that?

Last Edited: Thu. Oct 19, 2017 - 12:52 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_K_U_N_X wrote:
 I find times when int8_t is better and other times where char is better. 

Really? Can you give some examples?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_K_U_N_X wrote:
avr (sic) studio does not recognize int8_t

You mean Atmel Studio?

 

And you said you're using 6.2?

 

But what do you mean by, "does not recognise" ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I do not have any on hand no... my guess would be how it is being used? potential 'bugging' the code as it was used wrong like the case above.  Needless to say ill pay more attention to it now. 

 

could one say this?

char -127 to 127

int8_t  -128 127 (because of twos-c)?

 

You mean Atmel Studio?

 

And you said you're using 6.2?

yes and I mean "coloring" it. 

Last Edited: Thu. Oct 19, 2017 - 01:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You have to include stdint.h to get uint8_t and stuff like that.

 

edit: ah, you mean the syntax highlighting? Maybe you don't have the Visual Assist extension installed?

Last Edited: Thu. Oct 19, 2017 - 01:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Almost all AVR programs in Studio (gcc) start wit #include <avr/io.h> and amongst other things that includes <stdint.h> so the C99 types are always available. The usage should be:

 

char:    'a', 'Q', '£', etc

int8_t:  -128 .. +127

uint8_t: 0 .. 255

 

Or the other way round. For

 

'a', 'Q', '£', etc use char

-128 .. +127       use int8_t

0 .. 255           use uint8_t

 

It really as simple as that. So your question:

S_K_U_N_X wrote:
could one say this?

char -127 to 127

int8_t -128 127 (because of twos-c)?

Makes little sense because you wouldn't be storing -127 to 127 in "char". You would store char(acters) in char.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Makes little sense because you wouldn't be storing -127 to 127 in "char". You would store char(acters) in char.

Yes but correct me if I am missing something here? At run-time its all bits anyways.  What I'm trying to wrap my head around is how a char is any different then int8_t as far as the compiler is concerned when writing the machine language. I can completely understand the 'practice' of using a char for charters. In this case I'm speaking of (atmel's avr studio) Gcc specifically. Do they both not translate to a 8 bit registers? Thus, the concern is more so how we use the char in our syntax, or more the the point how we incorrectly use a char. Though using a char in place of int8_t and visa versa is still going to evaluate to 8 bits in the case of this compiler (obviously not all).

 

 

Maybe you don't have the Visual Assist extension installed?

ill try it thx. 

Last Edited: Thu. Oct 19, 2017 - 02:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

char as defined in my copy of K&R The C Programming Language, pg 195 A4.2 states: "especially whether the value is signed, is implimentation-dependent"

So how it is treated by a compiler will depend on the compiler, and so may very from compiler to compiler!

Yes it is 8 bit, but the range of values may be different in different compilers.

 

Jim

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_K_U_N_X wrote:
What I'm trying to wrap my head around is how a char is any different then int8_t as far as the compiler is concerned when writing the machine language.

Recall that you started the thread because "compilers are giving different results".  The advice that you have been given is to avoid that in the case of 8-bit variables, by being explicit and proper and not expecting the compiler to guess your intentions.  Because, as the standard says,

15 The three types char, signed char, and unsigned char are collectively called
the character types. The implementation shall define char to have the same range,
representation, and behavior as either signed char or unsigned char.35)
 

...

35) CHAR_MIN, defined in <limits.h>, will have one of the values 0 or SCHAR_MIN, and this can be
used to distinguish the two options. Irrespective of the choice made, char is a separate type from the
other two and is not compatible with either.

 

...

3 The integer promotions preserve value including sign. As discussed earlier, whether a
‘‘plain’’ char is treated as signed is implementation-defined.

 

 

So do you want to continue playing compiler-roulette, or do you want to write code with deterministic results?  "You make the call."

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

ki0bk wrote:
Yes it is 8 bit, but the range of values may be different in different compilers.

Not only that, but the toolchain might allow you to choose...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The suggestion of the use of char came about from sternst, so I was not sure of the exact reason. And yes received it later.  The reason for the tread was answered in the first two posts. I just wanted to open discussion about the char thing as it always intrigued me. As I stated I got mixed results. So what I want to get out of the char thing is, will I have any effect in my code performance or size of code using one or the other specific to my compiler. Thus far I seen none.  As I said I completly understand the the miss use of the variable being rather clumsy. 

 

Last Edited: Thu. Oct 19, 2017 - 05:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, you can post some code samples (simple, please) where this difference is observed, then we can take a look at the assembly output. We are fortunate to have a member of this forum that is an avr-gcc developer and he is often receptive to improve avr-gcc code generation based on weaknesses we sometimes detect.

But more often, there is nothing to improve except our own understanding of avr-gcc code generation. Then we can help the compiler to generate better code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_K_U_N_X wrote:
will I have any effect in my code performance or size of code using one or the other specific to my compiler.
It's not size/performance you should be worried about but signedness. The bottom line (if you don't want to use int8_t / uint8_t - that makes this a bit easier) is ALWAYS use one of "signed" or "unsigned" before "char" if the the use of the variable is to be for anything numeric and not just holding characters (that you don't offer perform arithmetic on!). So it's your choice. Do you always type "signed char xxx" or "unsigned char xxx" or do you use "int8_t xxx" or "uint8_t xxx".

 

Note that the latter have one further benefit - like "int" can be different sizes on different CPU architectures in theory that is true of char too. If you use int8_t or uint8_t then on ANY compiler there is a guarantee that what you are going to get are 8 bit wide variables. That may not necessarily be true for "char".

 

(having said that I haven't personally ever encountered a C compiler where char was not 8 bits wide).

Last Edited: Fri. Oct 20, 2017 - 09:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_K_U_N_X wrote:
I wrote an array mapper and the ending result is bugged

I don't think this is quite the right use of the noun 'bug'. Your code may have a 'bug' but one normally would not refer to a result being 'bugged'. In Australia, we'd say 'buggered'

Last Edited: Fri. Oct 20, 2017 - 04:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
 one normally would not refer to a result being 'bugged'.

Although we do talk about stuff being "de-bugged" !

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh come on now, its in the urban dictionary, it has to be right :)

 

case #4

https://www.urbandictionary.com/...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Y'all don't have to worry about this; not in Germany at least...

https://www.nbcnews.com/news/wor...

...a new study found that more than 75 percent of the insect population in Germany has declined in the last two decades. ...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am afraid I too am guilty of using char for uint8_t.  The box is checked in my Visual Studio toolchain under General for "Default type for char is unsigned".  I think that is the default in Atmel Studio.  I have always thought an unsigned char was a weird concept.  I had gotten some test code when I first started AVR programming at the byte level and it used char for a byte of any kind of data, and I kept doing it.  Lately, I have been thinking about changing my projects to use uint8_t instead of char for flags and such that have a numerical value, and now that Cliff has made his point I feel stupid for not doing it sooner.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I had a project with a variable "volatile uint8_t foo" in one file and "extern volatile char foo" in another file and it compiled fine.  I guess the compiler sees uint8_t and unsigned char as the same thing.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MarkThomas wrote:

I had a project with a variable "volatile uint8_t foo" in one file and "extern volatile char foo" in another file and it compiled fine.  I guess the compiler sees uint8_t and unsigned char as the same thing.

If both of those lines were in the same compilation unit (e.g., one file included the other), the compiler would give you an error that you're redefining "foo" as a different type.

 

Try putting "char foo" in one file and "extern long foo" in another file (with neither file including the other), and the compiler won't know that the "extern" declaration is wrong, but if you try to use "foo" as a long you're gonna have a bad time.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh, then the compiler doesn't compare types on an extern.  That's interesting.  

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In Atmel avr Studio char is always signed or at least the default from v4 to v6.  All of the project I have used have sampled from use char, uchar or unsigned char. This is why I got in to the bad practice.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MarkThomas wrote:
Oh, then the compiler doesn't compare types on an extern.

No, simply because each file is compiled on it's own. The compiler never checks against other source files (or, to use the more correct term, "compilation units"). The compiler emits object code for each compilation unit separately. In that object code type information might be (or is) more generic.

 

It is the task of the linker to combine the object files into one executable, and it does not know anything about the original source code. It only knows what is in the object files.

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MarkThomas wrote:
Oh, then the compiler doesn't compare types on an extern. 

As Johan explained, it can only compare what it can see.

 

That's why it's good practice to #include the header with the extern declaration in the .c file with the definition - so that the compiler does then get to see both and can emit a diagnostic if they don't match.

 

EDIT

 

See, for example: http://www.keil.com/forum/655/de...

 

and note that this has nothing specifically to do with AVR

Last Edited: Sat. Oct 21, 2017 - 12:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
That's why it's good practice to #include the header with the extern declaration in the .c file with the definition - so that the compiler does then get to see both and can emit a diagnostic if they don't match.

I see.  I moved the extern in my code and tested it, and you are indeed correct.  Thanks!

 

JohanEkdahl wrote:
No, simply because each file is compiled on it's own.  It is the task of the linker to combine the object files into one executable

Thanks Johan.  That is clear now.

 

S_K_U_N_X wrote:
In Atmel avr Studio char is always signed or at least the default from v4 to v6.

You can change that if it is not the default.  I don't remember checking the box for unsigned as the default as shown below.  I thought it came checked in AS5.2 - AS7, but my memory is pretty full and I don't remember things as well as I did as a younger man.