uint32_t weirdness

Arduino 1.6.5 - MEGA2560

I have a simple routine to convert a Hexadecimal string to a uint32_t value, but it is not generating the expected value. Here is the code, and a debug Serial stream.

uint32_t htoli(const char inbuf[]){
uint32_t b=0;
uint16_t a;
uint8_t c;
Serial.print("htoli(\"");
Serial.print(inbuf);
Serial.print("\")=\n");
bool err=false;
while((c=inbuf[a++])&&!err){
	c = toupper(c);
	Serial.print(" c(chr)=");
	Serial.print((char)c);
	if(((c>='0')&&(c<='9'))||((c>='A')&&(c<='F'))){
		Serial.print(" b=");
		Serial.print(b,DEC);
		Serial.print(" ");
		b=b*16;
		Serial.print(" after mul b=");
		Serial.print(b,DEC);
		Serial.print(" c(dec)=");
	  c= c-48;
		if(c>9)c=c-7;
		b = b + c;
		Serial.print(c,DEC);
		Serial.print(", after add (b=b+c) b=");
		Serial.print(b,DEC);
		Serial.print("\n");
		}
	else {
		Serial.print("error? ");
		err=true;
		}
	}
//if(err) b=0;
Serial.print("result =");
Serial.println(b,DEC);
return b;
}

Here is the Debug out:

htoli("800")=
 c(chr)=8 b=0  after mul b=0 c(dec)=8, after add (b=b+c) b=8
 c(chr)=0 b=0  after mul b=0 c(dec)=0, after add (b=b+c) b=0
 c(chr)=0 b=0  after mul b=0 c(dec)=0, after add (b=b+c) b=0
result =0

Can anyone tell me why I can't find the problem?

Somewhere between the first and second iteration of the while(), the value of b is reset to Zero?

Chuck.

What is the initial value of 'a' ?

Pete

Why not use strtol()?

el_supremo:
What is the initial value of 'a' ?

Pete

I agree that it should be initialized, but based on the values of 'c' it is init'd to 0.

Can you see any reason that 'b' would be reset to zero?

Chuck.

jremington:
Why not use strtol()?

It might be easier, but I want to know why this code fails. If this simple coding is generating incorrect machine code I want to Know.

If there is some known uint32_t interaction faults, I would like to know them.

I am in the middle of a WebServer project that uses a Mega, with EEPROM, SPI Flash, SPI Ram, and SDcards for storage.

This code that exploded was called by my EEProm format routine, I have constructed a simple FAT file system, and a web server with SSI functionality. I plan on using it to capture sensor data from other Arduino IOT devices, Something like SparkFun's Data service. Something that can act as a repository for other devices.

Currently I am able to upload and download files with CURL.exe and of course Web browser access, I was working on the POST multipart/form-data format when this error cropped up. I want to squash it, at least understand what is going wrong so that I can defensively code around problem.

Chuck.

I agree that it should be initialized,

Definitely.

but based on the values of 'c' it is init'd to 0.

That's not a comforting thought. A bug is a bug. Fix it, and then if there are still problems you will know that at least the initial value of 'a' is not causing any of them.

Based on my quick test, not initializing 'a' is the problem.

Pete

el_supremo:
Definitely.
That's not a comforting thought. A bug is a bug. Fix it, and then if there are still problems you will know that at least the initial value of 'a' is not causing any of them.

Based on my quick test, not initializing 'a' is the problem.

Pete

Pete, you are correct, Initializing 'a' does 'solve' the problem, but how did initializing 'a' change the code generated generated in the while() loop?

htoli("800")=
 c(chr)=8 b=0  after mul b=0 c(dec)=8, after add (b=b+c) b=8
 c(chr)=0 b=8  after mul b=128 c(dec)=0, after add (b=b+c) b=128
 c(chr)=0 b=128  after mul b=2048 c(dec)=0, after add (b=b+c) b=2048
result =2048

Chuck.

p.s. still lost in the Dark!

If this simple coding is generating incorrect machine code

Extremely unlikely. There are millions of people who use avr-gcc every day and genuine compiler bugs are few, far between and difficult to discover.

chucktodd:
Pete, you are correct, Initializing 'a' does 'solve' the problem, but how did initializing 'a' change the code generated generated in the while() loop?

It didn't change the code generated. a had some garbage value left over in it when it was created and not initialized. Since it wasn't 0, it simply wasn't reading the bytes you thought it would.

jremington:
Extremely unlikely. There are millions of people who use avr-gcc every day and genuine compiler bugs are few, far between and difficult to discover.

Maybe not a fatal flaw, but it did produce unexpected results.

How could not initializing an index variable cause a different variable to change its value?

I could see if the index variable was being used to change a memory structure, but the index variable was performing correctly, there was a side effect.

Chuck.

chucktodd:
How could not initializing an index variable cause a different variable to change its value?

It doesn't. You weren't reading where you thought you were. Imagine that a started out with a 1 in it. You'd never see the 8 and you'd parse the two 0's and get 0 as an answer. You weren't reading in the "800". You were probably reading somewhere off in who knows what part of memory.

To prove it, go back to your old code and print a so you can see what value it was getting. Now go and use that value to initialize a and see that you get the exact same wrong result. What if a was getting the initial value of 64? What character was in inbuf[64]? That's the character being parsed, not your "800"

Delta_G:
It didn't change the code generated. a had some garbage value left over in it when it was created and not initialized. Since it wasn't 0, it simply wasn't reading the bytes you thought it would.

No,
as the debug output shown, the value of 'c' was changing in the expected manner. First it was the character '8' then the character '0', then the character '0'.

Whatever value 'a' had, (I think it started at zero), it was indexing through the char[] correctly. If it was pointed somewhere else,

htoli("800")=
c(chr)=8 b=0  after mul b=0 c(dec)=8, after add (b=b+c) b=8
c(chr)=0 b=0  after mul b=0 c(dec)=0, after add (b=b+c) b=0
c(chr)=0 b=0  after mul b=0 c(dec)=0, after add (b=b+c) b=0
result =0

the c(chr)= value would show something else. Between the c(chr) and c(dec) the code subtracted 48 and conditionally another 7 if necessary.

This exposes an edge condition, I want to know were the edge is before I place my feet. :slight_smile:

Chuck.

Agreed, the results are unexpected. The surprising thing is that when not initializing "a", it appears that the array is still indexed correctly, that is the correct character appears to be retrieved.

The unexpected behavior probably has to do with how the compiler optimizes the code with regard to initialized versus uninitialized indices.

When I suspect an optimization problem, I turn it off. That is required anyway when debugging, because the compiler can put optimized code anywhere it "wants" as long as the end result is correct.

Delta_G:
It doesn't. You weren't reading where you thought you were. Imagine that a started out with a 1 in it. You'd never see the 8 and you'd parse the two 0's and get 0 as an answer. You weren't reading in the "800". You were probably reading somewhere off in who knows what part of memory.

To prove it, go back to your old code and print a so you can see what value it was getting. Now go and use that value to initialize a and see that you get the exact same wrong result. What if a was getting the initial value of 64? What character was in inbuf[64]? That's the character being parsed, not your "800"

Debug results with 'a' initialized:

htoli("64")=
 c(chr)=6 b=0  after mul b=0 c(dec)=6, after add (b=b+c) b=6
 c(chr)=4 b=6  after mul b=96 c(dec)=4, after add (b=b+c) b=100
result =100
returned value of htoli("64")=100
htoli("64")=
 c(chr)=6 b=0  after mul b=0 c(dec)=6, after add (b=b+c) b=6
 c(chr)=4 b=6  after mul b=96 c(dec)=4, after add (b=b+c) b=100
result =100
returned value of htoli("buf")=100

the calling code:

char buf[10];
uint32_t bigValue = htoli("64");
Serial.print("returned value of htoli(\"64\")=");
Serial.println(bigValue,DEC);
sprintf(buf,"64");
bigValue = htoli(buf);
Serial.print("returned value of htoli(\"buf\")=");
Serial.println(bigValue,DEC);

Debug output with the initialization code for 'a' removed:

htoli("64")=
 c(chr)=6 b=0  after mul b=0 c(dec)=6, after add (b=b+c) b=6
 c(chr)=4 b=0  after mul b=0 c(dec)=4, after add (b=b+c) b=4
result =0
returned value of htoli("64")=0
htoli("64")=
 c(chr)=6 b=0  after mul b=0 c(dec)=6, after add (b=b+c) b=6
 c(chr)=4 b=0  after mul b=0 c(dec)=4, after add (b=b+c) b=4
result =0
returned value of htoli("buf")=0

The code that produces this error:

uint32_t htoli(const char inbuf[]){
uint32_t b=0;
uint16_t a;
uint8_t c;
Serial.print("htoli(\"");
Serial.print(inbuf);
Serial.print("\")=\n");
bool err=false;
while((c=inbuf[a++])&&!err){
 c = toupper(c);
 Serial.print(" c(chr)=");
 Serial.print((char)c);

 if(((c>='0')&&(c<='9'))||((c>='A')&&(c<='F'))){
 Serial.print(" b=");
 Serial.print(b,DEC);
 Serial.print(" ");

 b=b*16;
 Serial.print(" after mul b=");
 Serial.print(b,DEC);
 Serial.print(" c(dec)=");

  c= c-48;
 if(c>9)c=c-7;
 b = b + c;
 Serial.print(c,DEC);
 Serial.print(", after add (b=b+c) b=");
 Serial.print(b,DEC);
 Serial.print("\n");

 }
 else {
 Serial.print("error? ");
 err=true;
 }
 }

Serial.print("result =");
Serial.println(b,DEC);

return b;
}

This is a reproducible error. Something is wrong.

Chuck.

Why not print a since that value seems to be the source of at least some part of the problem.

jremington:
Agreed, the results are unexpected. The surprising thing is that when not initializing "a", it appears that the array is still indexed correctly, that is the correct character appears to be retrieved.

The unexpected behavior probably has to do with how the compiler optimizes the code with regard to initialized versus uninitialized indices.

When I suspect an optimization problem, I turn it off. That is required anyway when debugging, because the compiler can put optimized code anywhere it "wants" as long as the end result is correct.

I agree with what you are saying, Somehow the compiler is changing the loop to an incorrect form just because 'a' was never initialized.

It is probably allocating 'a' to a register that is colliding with the 'b' value, maybe it is re-initializing 'b' every time through the loop.

Is there anyway I can get a LST of of the optimized code? or a reverse assembly of the HEX?

Chuck.

maybe it is re-initializing 'b' every time through the loop.

additional compiler weirdness

if b is declared static uint32_t the function works even when "a" and "c" are not initialized.

Objdump time. See what it's being compiled into.

Delta_G:
Why not print a since that value seems to be the source of at least some part of the problem.

Here is the debug printing out 'a'

htoli("64")=
 after inc a=1 c(chr)=6 b=0  after mul b=0 c(dec)=6, after add (b=b+c) b=6
 after inc a=2 c(chr)=4 b=0  after mul b=0 c(dec)=4, after add (b=b+c) b=4
result =0

returned value of htoli("64")=0
htoli("64")=
 after inc a=1 c(chr)=6 b=0  after mul b=0 c(dec)=6, after add (b=b+c) b=6
 after inc a=2 c(chr)=4 b=0  after mul b=0 c(dec)=4, after add (b=b+c) b=4
result =0
returned value of htoli(buf)=0

new code with debug print out of 'a'

uint32_t htoli(const char inbuf[]){
uint32_t b=0;
uint16_t a;
uint8_t c;
Serial.print("htoli(\"");
Serial.print(inbuf);
Serial.print("\")=\n");
bool err=false;
while((c=inbuf[a++])&&!err){
 Serial.print(" after inc a=");
 Serial.print(a,DEC);
 c = toupper(c);
 Serial.print(" c(chr)=");
 Serial.print((char)c);

 if(((c>='0')&&(c<='9'))||((c>='A')&&(c<='F'))){
 Serial.print(" b=");
 Serial.print(b,DEC);
 Serial.print(" ");

 b=b*16;
 Serial.print(" after mul b=");
 Serial.print(b,DEC);
 Serial.print(" c(dec)=");

  c= c-48;
 if(c>9)c=c-7;
 b = b + c;
 Serial.print(c,DEC);
 Serial.print(", after add (b=b+c) b=");
 Serial.print(b,DEC);
 Serial.print("\n");

 }
 else {
 Serial.print("error? ");
 err=true;
 }
 }

Serial.print("result =");
Serial.println(b,DEC);

return b;
}

'a' looks to be incrementing correctly, but 'b' is still being re-initialized every time through the loop.

Chuck.

Delta_G:
Objdump time. See what it's being compiled into.

Can you point me somewhere that will instruct me on how to use Objdump, and where to acquire it?

Is it included with the tool chain, install with 1.6.5?

Chuck.