Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > 64-bit integers where the implementation supports max 32-bit ints

Reply
Thread Tools

64-bit integers where the implementation supports max 32-bit ints

 
 
James Harris
Guest
Posts: n/a
 
      08-05-2013
On a 16-bit C compiler which supports up to 32-bit integers but no larger
how feasible is it to support 64-bit integer values and is there a good way
to do so? Best I can think of is to pass structs around principally because,
AIUI, they can be returned from functions. But is there a better way?

Such a struct would be along the lines of

struct custom64 {
uint32_t low;
uint32_t high;
};
typedef struct custom64 custom64_t;

(For the 64-bit compiler this would instead be "typedef uint64_t
custom64_t;".)

I know I could pass around pointers-to-64-bit-integer but that would
probably result in some cases of needing malloc to store the values and lead
to more complexity. So structs seem better if they would work. I can live
with some inconvenience like not being able to specify literals and not
having printf formats for them.

This is for writing some C where it would be a big help if some of the
source code modules could be compiled to 16-bit object code and to 64-bit
object code and yet still work on 64-bit integers where necessary. So if it
were possible to write the following and just have the above different
definitions of the custom64_t type that would be especially useful.

custom64_t val1, val2;
val1 = func(val2);

I think I'll only need such explicitly long integers in rare cases and won't
need to do much arithmetic on them so it probably wouldn't be too burdensome
to manipulate the few references by callable routines if necessary. In other
words I can live without

val1 - val2

and replace it with

custom64_subtract(val1, val2);

It's early days but I think the main issues will be declaring them and
passing them to and from other functions. Arithmetic would be nice-to-have
but I cannot see any way to do that.

BTW, I should say I'm sure there are extensive libraries for wide number
manipulation but they are not what I want. Something short and simple that
will fit in a few lines would be much preferable to something pre-written
and extensive.

Am I on the right lines? Is there a 'standard' way to do stuff like this in
C?

James


 
Reply With Quote
 
 
 
 
Shao Miller
Guest
Posts: n/a
 
      08-05-2013
On 8/5/2013 13:07, James Harris wrote:
> On a 16-bit C compiler which supports up to 32-bit integers but no larger
> how feasible is it to support 64-bit integer values and is there a good way
> to do so? Best I can think of is to pass structs around principally because,
> AIUI, they can be returned from functions. But is there a better way?
>
> Such a struct would be along the lines of
>
> struct custom64 {
> uint32_t low;
> uint32_t high;
> };
> typedef struct custom64 custom64_t;
>
> (For the 64-bit compiler this would instead be "typedef uint64_t
> custom64_t;".)
>
> I know I could pass around pointers-to-64-bit-integer but that would
> probably result in some cases of needing malloc to store the values and lead
> to more complexity. So structs seem better if they would work. I can live
> with some inconvenience like not being able to specify literals and not
> having printf formats for them.
>
> This is for writing some C where it would be a big help if some of the
> source code modules could be compiled to 16-bit object code and to 64-bit
> object code and yet still work on 64-bit integers where necessary. So if it
> were possible to write the following and just have the above different
> definitions of the custom64_t type that would be especially useful.
>
> custom64_t val1, val2;
> val1 = func(val2);
>
> I think I'll only need such explicitly long integers in rare cases and won't
> need to do much arithmetic on them so it probably wouldn't be too burdensome
> to manipulate the few references by callable routines if necessary. In other
> words I can live without
>
> val1 - val2
>
> and replace it with
>
> custom64_subtract(val1, val2);
>
> It's early days but I think the main issues will be declaring them and
> passing them to and from other functions. Arithmetic would be nice-to-have
> but I cannot see any way to do that.
>
> BTW, I should say I'm sure there are extensive libraries for wide number
> manipulation but they are not what I want. Something short and simple that
> will fit in a few lines would be much preferable to something pre-written
> and extensive.
>
> Am I on the right lines? Is there a 'standard' way to do stuff like this in
> C?
>


Windows has 'LARGE_INTEGER', along these lines:


http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

--
- Shao Miller
--
"Thank you for the kind words; those are the kind of words I like to hear.

Cheerily," -- Richard Harter
 
Reply With Quote
 
 
 
 
glen herrmannsfeldt
Guest
Posts: n/a
 
      08-05-2013
James Harris <(E-Mail Removed)> wrote:

> On a 16-bit C compiler which supports up to 32-bit integers but no larger
> how feasible is it to support 64-bit integer values and is there a good way
> to do so? Best I can think of is to pass structs around principally because,
> AIUI, they can be returned from functions. But is there a better way?


Struct is a fine way. Many 16 bit compilers do 32 bit operations
by subroutine call, as it is too much work to do inline.
(Some do + and - inline, * and / by call.)

All are easy to write except divide. The divide algorithm is
in Knuth's "The Art of Computer Programming", I believe volume 2,
but you should check.

-- glen
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      08-05-2013
"James Harris" <(E-Mail Removed)> writes:
> On a 16-bit C compiler which supports up to 32-bit integers but no larger
> how feasible is it to support 64-bit integer values and is there a good way
> to do so? Best I can think of is to pass structs around principally because,
> AIUI, they can be returned from functions. But is there a better way?


Since 1999, the ISO C standard has required the type "long long" to be
at least 64 bits wide. Strictly speaking, if you're using a compiler
that doesn't support "long long", then you're not using a conforming C
compiler. To know how to work around that, you'd have to know what
other language features it doesn't support (such as, say, passing
structs by value).

If you're working with a compiler that conforms reasonably well to C90,
which didn't require long long or any 64-bit integer type, then you do
have a lot more

> Such a struct would be along the lines of
>
> struct custom64 {
> uint32_t low;
> uint32_t high;
> };
> typedef struct custom64 custom64_t;
>
> (For the 64-bit compiler this would instead be "typedef uint64_t
> custom64_t;".)


If you use "typedef uint64_t custom64_t;" for compilers that support
64-bit integers, it will be easy to write code that assumes custom64_t
is an integer type, which will break on your non-64-bit platform. You
might be better off defining it as a struct with a single uint64_t
member, and writing alternative functions/macros to perform operations
on them.

The name "uint32_t" was added by the same standard (C99) that mandated
the existence of "long long". Do you have a C90 compiler that supports
uint32_t as an extension? If not, you might need to define uint32_t
yourself.

If you care about the representation of your custom64_t mapping onto a
64-bit integer, endianness is going to be an issue. You might want to
use an #ifdef to control the order of the "low' and "high" members. If
the representation is never stored in a file or transmitted, that
probably doesn't matter.

[...]

> BTW, I should say I'm sure there are extensive libraries for wide number
> manipulation but they are not what I want. Something short and simple that
> will fit in a few lines would be much preferable to something pre-written
> and extensive.
>
> Am I on the right lines? Is there a 'standard' way to do stuff like this in
> C?


Yes, I think you're on the right track. If you have 32-bit unsigned
integers but not 64-bit unsigned integers, a struct like you've defined
is a good way to emulate them. Implementing the operations you need is,
of course, left as an exercise. And probably you don't need to
implement *all* the operations; if you never divide 64-bit integers, you
don't have to implement 64-bit division.

--
Keith Thompson (The_Other_Keith) http://www.velocityreviews.com/forums/(E-Mail Removed) <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
James Harris
Guest
Posts: n/a
 
      08-05-2013

"Keith Thompson" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> "James Harris" <(E-Mail Removed)> writes:
>> On a 16-bit C compiler which supports up to 32-bit integers but no larger
>> how feasible is it to support 64-bit integer values and is there a good
>> way
>> to do so? Best I can think of is to pass structs around principally
>> because,
>> AIUI, they can be returned from functions. But is there a better way?

>
> Since 1999, the ISO C standard has required the type "long long" to be
> at least 64 bits wide. Strictly speaking, if you're using a compiler
> that doesn't support "long long", then you're not using a conforming C
> compiler. To know how to work around that, you'd have to know what
> other language features it doesn't support (such as, say, passing
> structs by value).


True. The 16-bit compiler could be a problem in this - it already has been
in other ways.

I have checked that it does compile structs as return values ... but not run
the code yet....

> If you're working with a compiler that conforms reasonably well to C90,
> which didn't require long long or any 64-bit integer type, then you do
> have a lot more
>
>> Such a struct would be along the lines of
>>
>> struct custom64 {
>> uint32_t low;
>> uint32_t high;
>> };
>> typedef struct custom64 custom64_t;
>>
>> (For the 64-bit compiler this would instead be "typedef uint64_t
>> custom64_t;".)

>
> If you use "typedef uint64_t custom64_t;" for compilers that support
> 64-bit integers, it will be easy to write code that assumes custom64_t
> is an integer type, which will break on your non-64-bit platform. You
> might be better off defining it as a struct with a single uint64_t
> member, and writing alternative functions/macros to perform operations
> on them.


Good point. I currently compile 16-bit and 32-bit at the same time but don't
yet have a way to compile 64-bit.... Actually, having said that the 32-bit
compiler represents 64-bit numbers as long longs (and the 16-bit compiler
represents them as structs) so even though I am not compiling to 64-bit yet
the issue you raised should be covered.

FWIW the section of the header which defines the 64-bit types is currently
as follows. It's not fully tested yet.

/*
* 64-bit integers
*/

#if INT_MAX == 0x7fffffffffffffff
typedef signed int s64_t;
#elif LONG_MAX == 0x7fffffffffffffff
typedef signed long s64_t;
#elif LLONG_MAX == 0x7fffffffffffffff
typedef signed long long s64_t;
#else
typedef struct {u32_t low; s32_t high;} s64_t; /* Limited use */
#endif

#if UINT_MAX == 0xffffffffffffffff
typedef unsigned int u64_t;
#elif ULONG_MAX == 0xffffffffffffffff
typedef unsigned long u64_t;
#elif ULLONG_MAX == 0xffffffffffffffff
typedef unsigned long long u64_t;
#else
typedef struct {u32_t low; u32_t high;} u64_t; /* Limited use */
#endif


> The name "uint32_t" was added by the same standard (C99) that mandated
> the existence of "long long". Do you have a C90 compiler that supports
> uint32_t as an extension? If not, you might need to define uint32_t
> yourself.


The 16-bit compiler doesn't have those definitions but the 32-bit compiler
does. So I have chosen different names and come up with a header which
includes the following. There are similar sections for other integer widths.
The 32-bit stuff is simpler than that for 64-bit, above.

/*
* 32-bit integers
*/

#if INT_MAX == 0x7fffffff
typedef signed int s32_t;
#elif LONG_MAX == 0x7fffffff
typedef signed long s32_t;
#else
# error "No type candidate for s32_t"
#endif

#if UINT_MAX == 0xffffffff
typedef unsigned int u32_t;
#elif ULONG_MAX == 0xffffffff
typedef unsigned long u32_t;
#else
# error "No type candidate for u32_t"
#endif

>
> If you care about the representation of your custom64_t mapping onto a
> 64-bit integer, endianness is going to be an issue. You might want to
> use an #ifdef to control the order of the "low' and "high" members. If
> the representation is never stored in a file or transmitted, that
> probably doesn't matter.


FWIW I hate #ifs which select different pieces of code. They seem to me to
be all sorts of wrong. What I am trying to do is write the code as small
modules and pick the correct module at link time. That's a topic in itself.

James


 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      08-06-2013
"James Harris" <(E-Mail Removed)> writes:
> "Keith Thompson" <(E-Mail Removed)> wrote in message
> news:(E-Mail Removed)...

[...]
> FWIW the section of the header which defines the 64-bit types is currently
> as follows. It's not fully tested yet.
>
> /*
> * 64-bit integers
> */
>
> #if INT_MAX == 0x7fffffffffffffff
> typedef signed int s64_t;
> #elif LONG_MAX == 0x7fffffffffffffff
> typedef signed long s64_t;
> #elif LLONG_MAX == 0x7fffffffffffffff
> typedef signed long long s64_t;
> #else
> typedef struct {u32_t low; s32_t high;} s64_t; /* Limited use */
> #endif


This might cause problems if the 16-bit compiler's preprocessor can't
handle a 64-bit constant like 0x7fffffffffffffff. I'm not sure there's
a really good solution. But it's likely you'll only get some warnings,
and you can ignore them as long as it selects the right definition.

[...]
>> The name "uint32_t" was added by the same standard (C99) that mandated
>> the existence of "long long". Do you have a C90 compiler that supports
>> uint32_t as an extension? If not, you might need to define uint32_t
>> yourself.

>
> The 16-bit compiler doesn't have those definitions but the 32-bit compiler
> does. So I have chosen different names and come up with a header which
> includes the following. There are similar sections for other integer widths.
> The 32-bit stuff is simpler than that for 64-bit, above.


Hmm. Personally, I'd be inclined to use the *same* names defined by
C99.

Some years ago, Doug Gwyn wrote a collection of files under the name
"q8" which provide an implementation of C99-specific headers (including
<stdint.h> for use with pre-C99 compilers. They're public domain, so
feel free to use them any way you like.

For example:

#if _STDC_VERSION__ >= 199901L
#include <stdint.h>
#else
#include <mystdint.h>
#endif

You can then freely use uint32_t and friends (except, of course, that
your uint64_t won't directly support arithmetic operations).

[...]

--
Keith Thompson (The_Other_Keith) (E-Mail Removed) <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
James Harris
Guest
Posts: n/a
 
      08-06-2013

"Keith Thompson" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> "James Harris" <(E-Mail Removed)> writes:
>> "Keith Thompson" <(E-Mail Removed)> wrote in message
>> news:(E-Mail Removed)...

> [...]
>> FWIW the section of the header which defines the 64-bit types is
>> currently
>> as follows. It's not fully tested yet.
>>
>> /*
>> * 64-bit integers
>> */
>>
>> #if INT_MAX == 0x7fffffffffffffff
>> typedef signed int s64_t;
>> #elif LONG_MAX == 0x7fffffffffffffff
>> typedef signed long s64_t;
>> #elif LLONG_MAX == 0x7fffffffffffffff
>> typedef signed long long s64_t;
>> #else
>> typedef struct {u32_t low; s32_t high;} s64_t; /* Limited use */
>> #endif

>
> This might cause problems if the 16-bit compiler's preprocessor can't
> handle a 64-bit constant like 0x7fffffffffffffff. I'm not sure there's
> a really good solution. But it's likely you'll only get some warnings,
> and you can ignore them as long as it selects the right definition.


The 16-bit C compilers I've tried are quirky in a number of ways. For the
case in point they don't complain about the big constants which, I agree, is
a bit odd. Hopefully the equality comparisons would help any such compiler
pick the else clause but I know not to rely on that working in all cases.

It's a pity there's no such thing as a preprocessor assert to check the
results - and, yes, I have seen various clever but convoluted attempts to
make one.


> [...]
>>> The name "uint32_t" was added by the same standard (C99) that mandated
>>> the existence of "long long". Do you have a C90 compiler that supports
>>> uint32_t as an extension? If not, you might need to define uint32_t
>>> yourself.

>>
>> The 16-bit compiler doesn't have those definitions but the 32-bit
>> compiler
>> does. So I have chosen different names and come up with a header which
>> includes the following. There are similar sections for other integer
>> widths.
>> The 32-bit stuff is simpler than that for 64-bit, above.

>
> Hmm. Personally, I'd be inclined to use the *same* names defined by
> C99.


I could see it that way. However, at present all names are clearly distinct
from those standard names so it's more obvious that "all errors are my own"!
In contrast to your #if selection below I can have just a single include,
i.e.

#include "my header"

I'll keep your recommendation in mind, though. I'm just trying this stuff
out as yet and may well come to a realisation that something else is a
better idea than what seems good at this early stage.

> Some years ago, Doug Gwyn wrote a collection of files under the name
> "q8" which provide an implementation of C99-specific headers (including
> <stdint.h> for use with pre-C99 compilers. They're public domain, so
> feel free to use them any way you like.


I looked at that before but didn't like the way the choices were made. The
Q8 header has lots of sections like

#if compiler x and target y
....
#elif compiler x and target z
....
etc

The Q8 selection process seems pretty fragile. As you suggest, it could be
copied and adapted but at present I prefer the selections to be based on
values in limits.h. That was recommended to me by folks in this newsgroup
and works really well. It depends entirely on real values rather than values
which should be the case for a given compiler and environment. That's got to
be better, right!

> For example:
>
> #if _STDC_VERSION__ >= 199901L
> #include <stdint.h>
> #else
> #include <mystdint.h>
> #endif
>
> You can then freely use uint32_t and friends (except, of course, that
> your uint64_t won't directly support arithmetic operations).


AIUI both <> and "" refer to implementation-defined places but wouldn't the
above normally have mystdint.h in quotes rather than angle brackets?

James


 
Reply With Quote
 
James Harris
Guest
Posts: n/a
 
      08-06-2013

"Keith Thompson" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> "James Harris" <(E-Mail Removed)> writes:
>> "Keith Thompson" <(E-Mail Removed)> wrote in message
>> news:(E-Mail Removed)...

> [...]
>> FWIW the section of the header which defines the 64-bit types is
>> currently
>> as follows. It's not fully tested yet.
>>
>> /*
>> * 64-bit integers
>> */
>>
>> #if INT_MAX == 0x7fffffffffffffff
>> typedef signed int s64_t;
>> #elif LONG_MAX == 0x7fffffffffffffff
>> typedef signed long s64_t;
>> #elif LLONG_MAX == 0x7fffffffffffffff
>> typedef signed long long s64_t;
>> #else
>> typedef struct {u32_t low; s32_t high;} s64_t; /* Limited use */
>> #endif

>
> This might cause problems if the 16-bit compiler's preprocessor can't
> handle a 64-bit constant like 0x7fffffffffffffff. I'm not sure there's
> a really good solution. But it's likely you'll only get some warnings,
> and you can ignore them as long as it selects the right definition.


Interestingly it *has* caused a problem - not on the 0x7f... tests but on
those for 0xff.... From tests it seems likely that the 16-bit preprocessor
keeps only the low 32 bits of the constant (which is understandable given
how it will convert such a number to binary) so a string of 16 Fs matches a
string of 8 Fs.

Fortunately the tests against 0x7f... work properly.

So I wonder if I could assign the unsigned values along with the signed ones
as in the following.

/*
* 64-bit integers
*/

#if INT_MAX == 0x7fffffffffffffff
typedef signed int s64_t;
typedef unsigned int u64_t;
#elif LONG_MAX == 0x7fffffffffffffff
typedef signed long s64_t;
typedef unsigned long u64_t;
#elif LLONG_MAX == 0x7fffffffffffffff
typedef signed long long s64_t;
typedef unsigned long long u64_t;
#else
typedef struct {u32_t low; s32_t high;} s64_t; /* Limited use */
typedef struct {u32_t low; u32_t high;} u64_t; /* Limited use */
#warning "Using structures for s64_t and u64_t"
#endif

In other words, is it safe to assume that <something>_MAX and
U<something>_MAX will be the same size but with the top bit set on the
latter? (If so I should probably select all signed and unsigned values in
the same way, i.e. based on the signed maxima.)

What fun.

James


 
Reply With Quote
 
James Kuyper
Guest
Posts: n/a
 
      08-06-2013
On 08/06/2013 06:15 AM, James Harris wrote:
....
> It's a pity there's no such thing as a preprocessor assert to check the
> results - and, yes, I have seen various clever but convoluted attempts to
> make one.


What does "preprocessor assert" mean to you, such that #if combined with
#error fails to qualify?

....
>> #if _STDC_VERSION__ >= 199901L
>> #include <stdint.h>
>> #else
>> #include <mystdint.h>
>> #endif
>>
>> You can then freely use uint32_t and friends (except, of course, that
>> your uint64_t won't directly support arithmetic operations).

>
> AIUI both <> and "" refer to implementation-defined places but wouldn't the
> above normally have mystdint.h in quotes rather than angle brackets?


Yes. While the locations that are searched are implementation-defined
for both forms, the key difference is that when you use "", it first
searches an implementation-defined set of places, and if that search
fails, falls back to searching the same set of places used by <>. In
other words, "" causes a search that is at least as wide as that caused
by <>, and on any reasonable implementation it will be a wider one,
generally including user-specified locations. Standard headers are
guaranteed to be found by either form, but a non-standard header might
be found only by "", so it's generally a good idea to reserve <> for
standard headers, and "" for everything else.
The POSIX headers are guaranteed, by the POSIX standard, to be found by
#include <>, so I count them as standard headers for this purpose. I
presume other operating systems might make similar guarantees.
--
James Kuyper
 
Reply With Quote
 
Malcolm McLean
Guest
Posts: n/a
 
      08-06-2013
On Monday, August 5, 2013 10:37:40 PM UTC+1, James Harris wrote:
> "Keith Thompson" <(E-Mail Removed)> wrote in message
>
> FWIW I hate #ifs which select different pieces of code. They seem to me to
> be all sorts of wrong. What I am trying to do is write the code as small
> modules and pick the correct module at link time. That's a topic in itself.
>

Yes, it means you have to test the code on a 16 bit compiler as well as a
32 bit one to know that it's correct, because effectively you've got two
programs sharing the same source file. Also the syntax is rather hard to
read.

stdint.h causes endless problems. ffmpeg uses it because it needs 64 bit
types, and it breaks on Visual Studio, I suspect deliberately. But it is
the right answer, everyone defining their own fixed-size type was worse.
A lot of fixed-size types are used unnecessarily, but they are sometimes
needed.

C should have a mul() function that returns the high portion of a value.
Most chips have a machine instruction which does this. But it doesn't, and
it's not easy to write a portable one.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
The node.js Community is Quietly Changing the Face of Open Source Rodrick Brown Python 2 04-17-2013 04:47 PM
Is there a difference between the use of the word montage vscollage Danny D. Digital Photography 8 04-15-2013 02:24 PM
Windows 8 - so bad it's hastening the death of the PC? ~misfit~ NZ Computing 18 04-15-2013 04:15 AM
Iterator Question for map of ints to set of ints uclamathguy@gmail.com C++ 3 04-03-2005 03:26 AM
ints ints ints and ints Skybuck Flying C Programming 24 07-10-2004 04:48 AM



Advertisments