Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C Programming (http://www.velocityreviews.com/forums/f42-c-programming.html)
-   -   3-byte ints (http://www.velocityreviews.com/forums/t315415-3-byte-ints.html)

Ed Morton 09-23-2003 02:19 PM

3-byte ints
 
I have 2 counters - one is required to be a 2-byte variable while the
other is required to be 3 bytes (not my choice, but I'm stuck with it!).
I've declared them as:

unsigned short small;
unsigned long large: 24;

First question - is that the best way to declare the "large" one to
ensure it's 3 bytes? Another suggestion I got was "unsigned char
large[3];" but that would be a little tougher to do arithmetic
operations on.

Now I need a general-purpose macro to increment them and I need to be
able to produce a report if the counter would overflow when incremented.
So I don't need to have separate macros for each type of counter, I've
done this by introducing a temporary unsigned long (no counter will be
larger than that) to store the original and then doing the increment
using the real counter (to ensure that overflow occurs when expected for
that type of counter) then testing if it rolled over by seeing if the
result is less than the original as in the "incCntr()" macro below:

#include <stdio.h>

typedef struct {
unsigned short small;
unsigned long large: 24;
} CNTRS;

#define incCntr(cntr,incr) \
do { \
unsigned long _tmp = (unsigned long)(cntr); \
(cntr) = (cntr) + (incr); \
if ((cntr) < _tmp) { \
printf("Overflow at %lu + %d\n",_tmp,(incr));\
(cntr) = _tmp; \
} \
printf("%lu -> %lu\n",_tmp,(unsigned long)(cntr));\
} while(0)

int main(void)
{
CNTRS cntrs;
cntrs.small = 65529;
cntrs.large = 16777210;
incCntr(cntrs.small,5);
incCntr(cntrs.small,5);
incCntr(cntrs.large,5);
incCntr(cntrs.large,5);
return 1;
}

Second question - anyone see any problems with doing the overflow test
this way or can suggest a better alternative?

When compiling I get this warning:

gcc -Wall -otst tst.c
tst.c: In function `main':
tst.c:26: warning: long unsigned int format, unsigned int arg (arg 3)
tst.c:27: warning: long unsigned int format, unsigned int arg (arg 3)

It's complaining about the "cntr" argument in the line:

printf("%lu -> %lu\n",_tmp,(unsigned long)(cntr));

Third question - why is the compiler apparently ignoring my cast and
complaining that "(unsigned long)(cntr)" is an unsigned int?

Fourth question - would there be any reason not to declare my "small"
counter as an unsigned long bit-field too, i.e.:

unsigned long small: 16;

for consistency?

FWIW, the result of running the program is what I expected:

tst
65529 -> 65534
Overflow at 65534 + 5
65534 -> 65534
16777210 -> 16777215
Overflow at 16777215 + 5
16777215 -> 16777215

Regards,

Ed.


Mark A. Odell 09-23-2003 02:35 PM

Re: 3-byte ints
 
Ed Morton <mortonAVOIDINGSPAM@lucent.com> wrote in
news:bkpkq1$q3@netnews.proxy.lucent.com:

> I have 2 counters - one is required to be a 2-byte variable while the
> other is required to be 3 bytes (not my choice, but I'm stuck with it!).
> I've declared them as:
>
> unsigned short small;
> unsigned long large: 24;


How does the bit field help you? Why not:

unsigned short small; // 16-bits on this platform
unsigned long large; // 32-bits on this platform

#define INCR_SMALL(s, i) do \
{ \
if (s + i <= UNSIGNED_SHORT_MAX) s += i; \
else printf("Overflow off " #s "+" #i "\n"); \
} while (0)

#define INCR_LARGE(l, i) do \
{ \
if (l + i <= 0x00FFFFFFUL) l += i; \
else printf("Overflow off " #l "+" #i "\n"); \
} while (0)

Note: above is untested.

--
- Mark ->
--

Mark A. Odell 09-23-2003 02:36 PM

Re: 3-byte ints
 
"Mark A. Odell" <nospam@embeddedfw.com> wrote in
news:Xns93FF6BC8F17CCopyrightMarkOdell@130.133.1.4 :

>
> #define INCR_SMALL(s, i) do \
> { \
> if ((unsigned long) s + i <= UNSIGNED_SHORT_MAX) s += i; \

^^^^^^^^^^^^^^^
Oops, need this.

--
- Mark ->
--

Kevin Bracey 09-23-2003 02:56 PM

Re: 3-byte ints
 
In message <bkpkq1$q3@netnews.proxy.lucent.com>
Ed Morton <mortonAVOIDINGSPAM@lucent.com> wrote:

> I have 2 counters - one is required to be a 2-byte variable while the
> other is required to be 3 bytes (not my choice, but I'm stuck with it!).
> I've declared them as:
>
> unsigned short small;
> unsigned long large: 24;
>
> First question - is that the best way to declare the "large" one to
> ensure it's 3 bytes?


Pretty much, assuming it's in a structure. The only things I'd say are:

1) C90 doesn't allow anything other than "int" and "unsigned int" for
bitfield types. C99 does allow implementations to offer other types
like "unsigned long"; presumably your implementation does - it's
a common extension.

2) The size of a bitfield can't exceed that of its type - if you did
change it to "unsigned int", you'd then have a requirement that
int was at least 24 bits (but you'd get a diagnostic if it wasn't).

> [ snip code ]
> Second question - anyone see any problems with doing the overflow test
> this way or can suggest a better alternative?


Looks fine to me. If you're using C99 you could use uintmax_t rather than
unsigned long, just in case you end up with larger bitfields in future.

I wouldn't return 1 from main though - that'll probably be interpreted as
an error condition by the calling environment.

> It's complaining about the "cntr" argument in the line:
>
> printf("%lu -> %lu\n",_tmp,(unsigned long)(cntr));
>
> Third question - why is the compiler apparently ignoring my cast and
> complaining that "(unsigned long)(cntr)" is an unsigned int?


Because it's buggy? Your code looks fine to me.

> Fourth question - would there be any reason not to declare my "small"
> counter as an unsigned long bit-field too, i.e.:
>
> unsigned long small: 16;
>
> for consistency?


Not really, modulo the comments about types above. It might be more portable
as it would guarantee exactly 16 bits, which 'short' wouldn't. But in
practice, I've seen compilers generate significantly different code for a
16-bit bitfield versus a short; it's not unlikely that 'short' may be more
optimised, either in code generation terms or just alignment.

--
Kevin Bracey, Principal Software Engineer
Tematic Ltd Tel: +44 (0) 1223 503464
182-190 Newmarket Road Fax: +44 (0) 1223 503458
Cambridge, CB5 8HE, United Kingdom WWW: http://www.tematic.com/

Ed Morton 09-23-2003 04:03 PM

Re: 3-byte ints
 


Mark A. Odell wrote:

> Ed Morton <mortonAVOIDINGSPAM@lucent.com> wrote in
> news:bkpkq1$q3@netnews.proxy.lucent.com:
>
>
>>I have 2 counters - one is required to be a 2-byte variable while the
>>other is required to be 3 bytes (not my choice, but I'm stuck with it!).
>>I've declared them as:
>>
>>unsigned short small;
>>unsigned long large: 24;

>
>
> How does the bit field help you? Why not:
>
> unsigned short small; // 16-bits on this platform
> unsigned long large; // 32-bits on this platform


<snip>

I have to pass this structure to some other code that's expecting
several fields each to be exactly 3 bytes.

> #define INCR_SMALL(s, i) do \
> { \
> if (s + i <= UNSIGNED_SHORT_MAX) s += i; \
> else printf("Overflow off " #s "+" #i "\n"); \
> } while (0)
>
> #define INCR_LARGE(l, i) do \
> { \
> if (l + i <= 0x00FFFFFFUL) l += i; \
> else printf("Overflow off " #l "+" #i "\n"); \
> } while (0)
>
> Note: above is untested.
>


To isolate the callers of the macro from the types of the counters, it'd
mean creating a separate macro for each counter (I actually have several
3-byte counters and several 2-byte counters), e.g.:

#define INCR_C1(cntrs,incr) INCR_SMALL(cntrs.small1,incr)
#define INCR_C2(cntrs,incr) INCR_SMALL(cntrs.small2,incr)
#define INCR_C3(cntrs,incr) INCR_LARGE(cntrs.large1,incr)
.....

I do prefer that to incrementing the counter first and then having to
reset it later.

Once I added in the cast to unsigned long for the "s + i", it worked.

Thanks.

Ed.


Simon Biber 09-23-2003 05:40 PM

Re: 3-byte ints
 
"Ed Morton" <mortonAVOIDINGSPAM@lucent.com> wrote:
> I have to pass this structure to some other code that's expecting
> several fields each to be exactly 3 bytes.


Most C implementations do not support exact 3 byte integer types.

If it needs to be laid out exactly so in memory, you can create
an array of unsigned characters.

void pack(unsigned char *three, unsigned long value)
{
assert(value < (1UL << 24));
three[0] = value & 0xFF;
three[1] = value >> 8 & 0xFF;
three[2] = value >> 16 & 0xFF;
}

unsigned long unpack(unsigned char *three)
{
return (unsigned long)three[0]
| (unsigned long)three[1] << 8
| (unsigned long)three[2] << 16;
}

These functions assume you will be packing 8 bits into each byte,
and using a little-endian packing layout.

--
Simon.



Ed Morton 09-23-2003 07:43 PM

Re: 3-byte ints
 


On 9/23/2003 12:40 PM, Simon Biber wrote:
> "Ed Morton" <mortonAVOIDINGSPAM@lucent.com> wrote:
>
>>I have to pass this structure to some other code that's expecting
>>several fields each to be exactly 3 bytes.

>
>
> Most C implementations do not support exact 3 byte integer types.


So, if I use:

unsigned long large: 24;

then the code may not work on my original platform and, even if it does, it
isn't portable, right? What kind of problems could I expect to see? Is there any
way to test whether or not I actually have a problem?

> If it needs to be laid out exactly so in memory, you can create
> an array of unsigned characters.

<snip>
> These functions assume you will be packing 8 bits into each byte,
> and using a little-endian packing layout.
>


Sounds like I'll be needing those.

Thanks,

Ed.


Peter Nilsson 09-24-2003 12:40 AM

Re: 3-byte ints
 
"Simon Biber" <sbiber@optushome.com.au> wrote in message news:<3f708588$0$4189$afc38c87@news.optusnet.com.a u>...
> "Ed Morton" <mortonAVOIDINGSPAM@lucent.com> wrote:
> > I have to pass this structure to some other code that's expecting
> > several fields each to be exactly 3 bytes.

>
> Most C implementations do not support exact 3 byte integer types.


Do any? :-)

> If it needs to be laid out exactly so in memory, you can create
> an array of unsigned characters.
>
> void pack(unsigned char *three, unsigned long value)
> {
> assert(value < (1UL << 24));
> three[0] = value & 0xFF;
> three[1] = value >> 8 & 0xFF;
> three[2] = value >> 16 & 0xFF;
> }
>
> unsigned long unpack(unsigned char *three)
> {
> return (unsigned long)three[0]
> | (unsigned long)three[1] << 8
> | (unsigned long)three[2] << 16;
> }
>
> These functions assume you will be packing 8 bits into each byte,


Why? I know we live in an octet world, but you can do this portably
with CHAR_BIT and UCHAR_MAX.

> and using a little-endian packing layout.


--
Peter

Simon Biber 09-24-2003 03:05 AM

Re: 3-byte ints
 
"Peter Nilsson" <airia@acay.com.au> wrote:
> "Simon Biber" <sbiber@optushome.com.au> wrote:
> > Most C implementations do not support exact 3 byte integer types.

>
> Do any? :-)


I don't know of any, but I've learnt not to make generalisations on
comp.lang.c as someone inevitably provides an example to the contrary.

--
Simon.



Simon Biber 09-24-2003 03:45 AM

Re: 3-byte ints
 
"Ed Morton" <mortonAVOIDINGSPAM@Lucent.com> wrote:
> So, if I use:
>
> unsigned long large: 24;


It's not portable to use 'unsigned long' as the base type for a bitfield; the
only portable types are 'int' and 'unsigned int'.

> then the code may not work on my original platform and, even if it does, it
> isn't portable, right? What kind of problems could I expect to see? Is there
> any way to test whether or not I actually have a problem?


You need to know the exact binary format expected, then conform to it.

A 24-bit bitfield will probably still take up four bytes, and you have no
control over exactly where and in what order the 24 bits are stored.

You can check it out on your computer by:
#include <stdio.h>

int main(void)
{
struct foo {unsigned long large : 24; } bar;
size_t i;

bar.large = 0xDEADBE;
printf("%lu\n", (long unsigned) sizeof bar);
for(i = 0; i < sizeof bar; i++)
{
printf("%02X ", ((unsigned char *)&bar)[i]);
}
putchar('\n');
return 0;
}

I get:
4
BE AD DE 61

Which indicates the bitfield is stored in the first three of four bytes, in
little-endian order, and that the fourth (padding) byte is uninitialised.
Your results may vary.

--
Simon.




All times are GMT. The time now is 07:36 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.