Non-standard Length Integer Handling

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

I am trying to handle a signed 24bit value im being passed by a peripheral. The code below works great for turning negative binary numbers into signed decimals but only upto about -1300 after that output is complete garbage.

My main issues are:
1) My MSB check does not work and I don't see why not, on a 24bit value you have 0:23 and 23 would be the signed bit. so value & (1<<23) should = 1

2) on the 15th iteration of the for loop when i=15 my factor jumps from 16384 to -32768 which is defiantly wrong. 2^14=16384 and 2^15=32768 so I thought I was reaching the end of my variable size and inadvertently signing it or something but when I change it to an unsigned long long (PLENTY of room) 2^15=18446744073709518848 and for the remaining 8 cycles factor=0.

#include 
#include 

long buffer, binary;
char str_buff[14];
char step;
char i, bit;
long factor;
long decimal;

#define MSB 23

int main()
{
	binary =  0b111100100100101111001100; // -898,100

	//buffer = binary;
	if((binary) & (1<<MSB)) buffer = ~(binary - 1);

	for(i = 0; i < 23; i++)
	{
		if((buffer) & (1<<i)) bit = 1;
		else bit = 0;
		factor = (1<<i); // 1<<i = 2^i
		decimal += bit*factor;
	}
		
		ltoa(decimal,str_buff,10);

	return 0;
}

All of this failed effort today is because I must use a signed 24bit value and using a 32bit variable results in it being non-signed.

My latest thought is of using a 32bit variable to store it and doing something like this:

if(value & 1<<23)
{
 value &= ~(1<<23);
 new = value;
 new |= (1<<31);
}

So its a signed 32bit but only holds 24bits of information.

Any suggestions on what I should do?

EDIT - Is their a way to view the binary value of a variable in the avr studio debugger? It can do Dec and Hex but I want to see my data being shifted in and how my shifts are operating.

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

Have you checked the asm to see if it's really doing what you want?

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

Im terrible with asm which is why I use c. I looked at it but I spent two hours and didn't understand much of anything. The asm commands are simple but I cant understand how such simplistic reg = this and if zeros do anything like my code.

I attached the asm if it helps out.

Attachment(s): 

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

You could try changing the 24bit var to a 32bit (val<<8) and then do your signed variable operations. Then just convert back to 24bit when appropriate.

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

Here is an example of my actual input and my goal of an output:

input: 0b1000001011100000100110
convert to dec: -2050010
do some math: 2050010/20480 = 100.098
format the output into sign/1000/100/10/1/./0.1/0.01/0.001/0.0001 with leading zero blanking

eg - 100.0980 will be my final output.

I have thought of using sprintf for formatting as it has all my options but its quite a beefy include...

The problem with storing the 24bit in a 32bit by shifting it <<8 is when my math is done and I >>8 it looses its sign again. Thats why I thought I would keep the data as the lower 24bits of the 32bit variable and just swap the MSB from the 24 to the 32bit but none of my if(value & 1<<23) statements do anything. what am I doing wrong?

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

Here is one solution for You:

Declare some structures and an union:

typedef struct word_s {
    unsigned char l;
    unsigned char h;
} word_t;

typedef struct dword_s {
    word_t l;
    word_t h;
} dword_t;

typedef union dword_u {
    dword_t dw;
    unsigned long l;
    long s;
} dwordu_t;

Then in the code

dwordu_t mydw
long longeger;

// Assign the bytes into the struct
mydw.dw.l.l = MyLswLow;
mydw.dw.l.h = MyLswHigh;
mydw.dw.h.l = MyMsb;
mydw.dw.h.h = 0;

// Check if this is negative
// Do sign extent if is
if (mydw.dw.h.l & 0x80) {
    mydw.dw.h.h = 0xFF;
}

// Now You can assign the value
longeger = mydw.s;

And in the reverse direction You just do the following:

dwordu_t mydw
long longeger;

longeger = VeryComplexArithmeticCalculation();
mydw.s = longeger;
MyLswLow = mydw.dw.l.l;
MyLswHigh = mydw.dw.l.h;
MyMsb = mydw.dw.h.l;

I am not a compiler so there may be some syntactical errors - hopefully the principle got clear.

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

Are you wanting to do sign extension on the 24 bit value passed from the a/d converter?

Assuming the msb set means negative ( not necessarily so on some Analog Devices converters):

if (value & 0x00800000L) //if msb of 24bit is set,
{
value |= 0xff000000L; //then sign extend
}

(edit) seems Eskoila says much the same thing.

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

Kartman wrote:

if (value & 0x00800000L) //if msb of 24bit is set,
{
value |= 0xff000000L; //then sign extend
}

Yes - but -

One has to put the 24 bit integer into the longeger first.....

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

eskoilola - Wow with all the shorthand on those structures (I actually don't completely understand unions and structures yet) it took a bit to get through but I think Kartmans simple method works easier. I assume your method is to avoid typecasting and compiler warnings...

Why does (value & 0x00800000L) work and yet (value & (1<<23)) not? I see them as the exact same thing..

Tomorrow I will re-write my code using 32bits and the sign extension and let you know how it goes. thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
(1<<i)

The result of this operation is a 16 bit number. as soon as i becomes larger than 15, the result will be 0. But I don't see the point of any of your code. You are taking 23 bits of one number and moving them bit by bit into another number. As Kartman says, all you really need is to sign extend into the upper 8 bits.

Regards,
Steve A.

The Board helps those that help themselves.

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

It is not just avoiding compiler warnings - it is to avoid making silly mistakes and make the code look nicer. It also allows the compiler to do better optimizations.

Method #1

uint8_t byte1;
uint8_t byte2;
uint8_t byte3;
int32_t mylong;

byte1 = FetchValue1();
byte2 = FetchValue2();
byte3 = FetchValue3();

mylong = (int32_t)0;
mylong |= (int32_t)byte1;
mylong |= (((int32_t)byte2)<<8);
mylong |= (((int32_t)byte3)<<16);
if ( byte3 & 0x80 ) {
     mylong != 0xF0000000;
}

Method #2

dwordu_t mydw;
int32_t mylong;
mydw.l.l = FetchValue1();
mydw.l.h = FetchValue2();
mydw.h.l = FetchValue3();
if ( mydw.h.l & 0x80 ) {
    mydw.h.h = 0xFF;
}
else  {
    mydw.h.h = 0;
}
mylong = mydw.s;

Now, there is a silly mistake in Method #1, can You spot it ?

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

Esky - you've got more than one mistake!

The byte manipulations were not required as the OP had another thread where I had shown how to shift bits into a long var. So it was given ( as far as I knew) that the input value was already a long but had to be sign extended.

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

Alright that was a million times easier than I was originally doing. Why re-invent the wheel yet I always seem to..

So I got my 24bit value stored in a 32bit variable, I do a sign extension if necessary, dump the value into a float so I can have my decimal places and do a simple division.

ltoa unfortunately truncates a float only giving me -360 and sprintf only give me output if I use 'ld' as the input type and then it prints the -7,391,542 very well and I can properly format it.

C++.com's reference page for sprintf says I should be able to use 'Lf' but when I change my string to sprintf(str_buff,"%+#4.4Lf",buffer); (exactly what I want by the way) sometimes it puts a "?" in my str_buff but mostly it does nothing. Compiler says:

Quote:
../Test.c:30: warning: format '%+#4.4Lf' expects type 'long double', but argument 3 has type 'float'

I have looked in these forums and a few mailing lists but cant find an example of floats on sprintf, can you give me an example of how to properly convert my float to a string?

#include 
#include 
#include  
#include 
#include 

int main()
{

	signed long binary; 
	float buffer;  
	char str_buff[10];

	binary =  0x8F36CA; // -7,391,542

	if(binary & 0x00800000L) // If binary Is Signed
	{
		binary |= 0xFF000000L; // Extend Sign To 32bits
	}

	buffer = binary; // Re-Cast So Division Has Decimals
	buffer = buffer / 20480; // -360.9151
	
	for(;;)
	{
		/* Somehow Format My Buffer Into:
					   '- 360.9151'             */

		ltoa(buffer,str_buff,10); // Truncates at -360
		sprintf(str_buff,"%+ld",binary); // No Output if "%+#4.4Lf"
	}

return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
C++.com's reference page for sprintf says I should be able to use 'Lf' but when I change my string to sprintf(str_buff,"%+#4.4Lf",buffer); (exactly what I want by the way) sometimes it puts a "?" in my str_buff but mostly it does nothing
Have you read the user manual for avr-gcc? Maybe the settings to control the types are different.

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

Kartman wrote:
Esky - you've got more than one mistake!

Aaaaaaargh !

Yeah - I am fully capable of generating errors out of nowhere ;)

Oh-Oh .... sorry about my confusion on this. Of course it is better to make things simple. Your solution is generally accepted, simple one.

I also have done some investigations on how gcc optimizes stuff when using structures and unions as compared with using the shift method. The difference is dramatic. However - the gcc fails to optimize too complex unions/structures so some precaution with those is always on place.

P.S.
My name is Esko.... ;)

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

Quote:
dump the value into a float

No real need to do this. Just use scaled integers. But if you are going to do floats, you need to use % f, not % Lf, and you need to link with the proper printf lib.

Regards,
Steve A.

The Board helps those that help themselves.

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

I have included this in my linker under edit project config -> custom options. "-Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt" and in Libraries I have "libprintf_flt.a libscanf_flt.a libm.a"

I have tried sprintf with % f and pretty much every combo of everything, with just % f the compiler expects a double instead of a float still.

I have read the avr-gcc reference but the thing is written with a tiny one line explanation in shorthand.

Quote:
int scanf ( const char * __fmt,
...
)

The function scanf performs formatted input from stream stdin.


for example this is not very useful when you have no idea how to use the function in the first place.

EDIT - Sorry this post took so long, I just discovered the error 400 bad request issue on this forum...

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

Quote:
int scanf ( const char * __fmt,
...
)

The function scanf performs formatted input from stream stdin.

See vfscanf() for details.

http://www.nongnu.org/avr-libc/u...

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

Forum ate my post :(

Long story short:
Use an explicit typecast to double - e.g. (double)varOfTypeFloat - inside your sprintf.

Also why are you looking at sscanf which parses floating point numbers from strings? You should only need printf_flt.

I used in AVR Studio 4:
-Wl,-u,vfprintf -lprintf_flt -lm
in 'Linker Options', not 'All' (or similar)
and did not manually add the .a files