Standard integer types vs <stdint.h> types

I

Ian Collins

CBFalconer said:
Using object types that are not universally available on any
machine, thus impeding portability. limits.h allows you to
automatically select a suitable type from short, int, long (maybe
long long) at compile time. The objective is suitable size with
minimum complexity.
Yes, but you can do that in your own stdint.h where the platform does
not provide one (which is what I do).
 
M

Malcolm McLean

Ian Collins said:
How so? If a platform is not explicitly listed as having 64 bit types,
the result is exactly the same as Richard's approach.

If it is, the chance of an unexpected bug is greatly reduced, because
the emulation library is not used. Where do think bugs will be, in the
processor's 64 bit instructions, or an emulation library?
Richard's code works on char arrays. Unless he's really done a bad job, the
code should work the same on any platform.
The alternative was to test for a 64-bit type, and use it if found. Which
means that you need to test on both 32 and 64 bit machines to a have
reasonable confidence that the functions are correct.
If your 64-bit machine has a turnaround time measured in hours, this is not
impossible, but it is extremely inconvenient.
 
M

Malcolm McLean

Paul Hsieh said:
His response is actually quite sad. Knowing that you can have this
extended (if not exactly convenient) integer implementation in C is
kind of useful at times when 32 bits just isn't enough, but you need
more portability that systems that implement 64 bits.
double can be used as a big integer with 53 bits of precision. That's a
perfectly simple, valid point.
 
I

Ian Collins

Malcolm said:
Richard's code works on char arrays. Unless he's really done a bad job,
the code should work the same on any platform.
The alternative was to test for a 64-bit type, and use it if found.
Which means that you need to test on both 32 and 64 bit machines to a
have reasonable confidence that the functions are correct.
If your 64-bit machine has a turnaround time measured in hours, this is
not impossible, but it is extremely inconvenient.
Do you test that your machine can do basic integer maths? Sounds a
little paranoid to me.
 
B

Ben Pfaff

Ian Collins said:
Do you test that your machine can do basic integer maths? Sounds a
little paranoid to me.

At least one C90 compiler has 64-bit extended integer types for
which some basic integer arithmetic operations are broken
(certain shift operations, if I recall correctly), so this isn't
necessarily just raw paranoia.
 
A

Army1987

James said:
Note: in principle, at least, you should be checking INT_MIN as well as
INT_MAX; there is no guaranteed relationship between the two values, and
unless your application is better served by an unsigned type, INT_MIN
should be at least as relevant as INT_MAX.
I think that the stuff in 6.2.6.2 imply that INT_MIN is either -INT_MAX or
-INT_MAX - 1.
 
J

James Kuyper

Army1987 said:
I think that the stuff in 6.2.6.2 imply that INT_MIN is either -INT_MAX or
-INT_MAX - 1.

That has been claimed; but I think it relies upon misinterpretation of
6.2.6.2p2. That clause says that it's implementation-defined whether a
particular bit pattern specified by the standard (it's different for 1's
complement than for the other two options) is a trap representation. On
that basis, people have claimed that this is the only bit pattern for a
signed integer type that is permitted to be atrap representation. That
seems like a non-sequitur to me.

It's also been claimed that having any other bit pattern involving value
or sign bits would violate the standard's requirements for the value of
each value and sign bit. Again, I don't see that - it seems to me that
so long as there are non-trap representations where those bits have the
specified value, those requirements are met, regardless of whether there
are also trap representations where those bits don't have that value.
 
H

Harald van Dijk

Army1987 said:
I think that the stuff in 6.2.6.2 imply that INT_MIN is either -INT_MAX
or -INT_MAX - 1.
[...]
It's also been claimed that having any other bit pattern involving value
or sign bits would violate the standard's requirements for the value of
each value and sign bit. Again, I don't see that - it seems to me that
so long as there are non-trap representations where those bits have the
specified value, those requirements are met, regardless of whether there
are also trap representations where those bits don't have that value.

The exact wording is:
"Each bit that is a value bit shall have the same value as the same bit
in the object representation of the corresponding unsigned type (if
there are M value bits in the signed type and N in the unsigned type,
then M <= N )."

Are you saying that an implementation is allowed to ignore this "shall"
in some cases not specified by the standard, as long as it respects it in
other cases? If you are, then could you explain to me how such an
implementation is any more conforming than one that allows
puts("Hello, world!");
but not any other arguments to puts? That's a bit of an extreme example,
I'll admit, but I really don't see a fundamental difference between the
two.
 
M

Michal Nazarewicz

CBFalconer said:
No, it isn't. It will fail miserably on a C90 system. Instead all
you need is a quick macro somewhere that will define MYTYPE as int
or long, depending on values in limits.h. This is system
independant, avoids oversized beasts, etc. etc. It doesn't need
sizes that are multiples of 8 either. How are you going to handle
a machine with a 9 bit byte, an 18 bit short, 36 bit int, 72 bit
long? Or other.

uint_least32_t
 
M

Michal Nazarewicz

CBFalconer said:
Using object types that are not universally available on any
machine, thus impeding portability. limits.h allows you to
automatically select a suitable type from short, int, long (maybe
long long) at compile time. The objective is suitable size with
minimum complexity.

You cannot choose "an integer type that is usually fastest to operate
with among all the integer types that have at least the specified
width." (C99, 7.18.1.3p1)
 
A

Army1987

Malcolm said:
Not all integers count things in memory. In this case a perfectly reasonable
strategy would be to number the errors consecutively and use the number to
index into an array of messages, but in fact the C standard hasn't imposed
that.
Ok.
Now suppose I have:

static const char *rank_names[] = { NULL, "Ace", "Deuce", "Three", "Four",
"Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King" };

struct card { enum rank rank; enum suit suit; };

Does the mere fact that I'm going to sooner or later write
rank_names[mycard.rank] mean that you'd consider the rank field in that
struct "something used to count things in memory"?
Would you be no more surprised to see that field declared as a size_t than
you are usually for other uses of that type? Do you include it in the
count when you say that "most integers are used to index arrays",
regardless of the fact that that field is going to be used for other
purposes too, having nothing to do with the memory of a computer, and that
I could simply replace that array with a switch statement with no
difference in semantics?

At the top of main in the last nontrivial program I've written, I declared
an int to temporarily hold the value of errno when I must preserve it
through library calls, an int used for a status (EXIT_SUCCESS or
EXIT_FAILURE), and a int to contain a number of digit to be passed in a
printf("%*lu", ...) call. Then I declared an array of BUFSIZ unsigned
chars to hold data to be written to/read from a binary file, two time_t's
(which happen to be long int on my system), an unsigned int to hold a seed
to call srand with, and a few size_t's used for sizes of things. The only
times I use the [] operator is with the argv array.
 
J

James Kuyper

Harald said:
The exact wording is:
"Each bit that is a value bit shall have the same value as the same bit
in the object representation of the corresponding unsigned type (if
there are M value bits in the signed type and N in the unsigned type,
then M <= N )."

Are you saying that an implementation is allowed to ignore this "shall"
in some cases not specified by the standard, as long as it respects it in
other cases?

Yes, very specifically, those unspecified cases allowed for by the
standard where a bit pattern constitutes a trap representation (6.2.6.1p5).
... If you are, then could you explain to me how such an
implementation is any more conforming than one that allows
puts("Hello, world!");
but not any other arguments to puts? That's a bit of an extreme example,
I'll admit, but I really don't see a fundamental difference between the
two.

The standard doesn't allow the array pointed at by "Hello, world!" to
contain trap representations.
 
C

CBFalconer

Ian said:
Yes, but you can do that in your own stdint.h where the platform
does not provide one (which is what I do).

Well, if the compiling end uses a C99 compiler, you can't replace
<stdint.h> [1], and thus have to use "stdint.h". Now you are
forbidden to redefine anything defined in <stdint.h>. So, if you
can adhere to all those requirements (and possibly others) and
produce a fully portable "stdint.h", go to it. Show us the result,
please.

[1] There may not even exist such a file, since the standard allows
the compiler to have all requirements built-in.
 
H

Harald van Dijk

Yes, very specifically, those unspecified cases allowed for by the
standard where a bit pattern constitutes a trap representation
(6.2.6.1p5).

Thanks for clarifying a bit further, I did misunderstand you. Supposing
int uses two's complement, has 31 value bits, one sign bit (represented
as the leftmost bit), and no padding bits, if
1000 0000 0000 0000 0000 0000 0000 0000
through
1000 0000 0000 0000 0000 0000 0000 1111
are trap representations (normally -2147483648 through -2147483633), then
this doesn't affect just representations, but also values: it breaks the
binary operators. For example, on this implementation, 0x7FFFFFFF ^ -1
would not be representable, even though the two operands are both valid
integers. I'm not sure if the standard actually disallows it (6.5p5 might
still apply), but from the rationale it's clear that it's meant to be
disallowed:

* The integer formats for printf suggest no provision for "invalid
integer" values, implying that any result of bitwise manipulation
produces an integer result which can be printed by printf.

Although I must admit that this is already incorrect because one
representation is explicitly allowed to be a trap representation...
 
H

Harald van Dijk

Ian said:
CBFalconer wrote:
Yes, but you can do that in your own stdint.h where the platform does
not provide one (which is what I do).

Well, if the compiling end uses a C99 compiler, you can't replace
<stdint.h> [1], and thus have to use "stdint.h".

No, if the compiling end uses a C99 implementation, the custom stdint.h
would simply end up unused, which is okay, since as you say, C99 already
provides <stdint.h>.
 
M

Malcolm McLean

Army1987 said:
Malcolm said:
Not all integers count things in memory. In this case a perfectly
reasonable strategy would be to number the errors consecutively and >>
use the number to index into an array of messages, but in fact the C
standard hasn't imposed that.
Ok.
Now suppose I have:

static const char *rank_names[] = { NULL, "Ace", "Deuce", "Three",
"Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen",
"King" };

struct card { enum rank rank; enum suit suit; };

Does the mere fact that I'm going to sooner or later write
rank_names[mycard.rank] mean that you'd consider the rank field in that
struct "something used to count things in memory"?
Yes. The cards names are quite clearly in an array. The rule of nine (see
website) states that when you have more than about nine cases, you stop
hardcoding logic for each one individually.
Would you be no more surprised to see that field declared as a size_t
than you are usually for other uses of that type? Do you include it in the
count when you say that "most integers are used to index arrays",
regardless of the fact that that field is going to be used for other
purposes too, having nothing to do with the memory of a computer, and >
that I could simply replace that array with a switch statement with no
difference in semantics?
Yes. The integer is ultimately used as an index. You mess about with it an
awful lot, but ultimately it is used to index into an array before it
produces any output. You could write the code with a switch, in which case
the statement would no longer be true. If your main goal in life is to prove
Malcolm wrong you can of course devote your career to writing C programs
that use no arrays whatsoever, and the code will work.
At the top of main in the last nontrivial program I've written, I declared
an int to temporarily hold the value of errno when I must preserve it
through library calls, an int used for a status (EXIT_SUCCESS or
EXIT_FAILURE), and a int to contain a number of digit to be passed in > a
printf("%*lu", ...) call. Then I declared an array of BUFSIZ unsigned
chars to hold data to be written to/read from a binary file, two time_t's
(which happen to be long int on my system), an unsigned int to hold a
seed to call srand with, and a few size_t's used for sizes of things. The
only times I use the [] operator is with the argv array.
That's a totally untypical program. Most C programs use arrays very heavily.
In fact it is the only basic data structure that has direct language
support.
Sometimes in older C programs you see pointer notation used where array
dereferencing would be clearer, I'll grant you. That's technically an
exception to my point, even though an integer will be used to count down the
travelling pointer.
 
J

James Kuyper

CBFalconer wrote:
....
Well, if the compiling end uses a C99 compiler, you can't replace
<stdint.h> [1], and thus have to use "stdint.h". Now you are
forbidden to redefine anything defined in <stdint.h>. So, if you
can adhere to all those requirements (and possibly others) and
produce a fully portable "stdint.h", go to it. Show us the result,
please.

You can create a file named stdint.h, arrange for it to be used when an
implementation doesn't provide it's own stdint.h, and make sure it
doesn't get used when the implementation does provide one. How can you
"arrange" these things? There's no portable answer. It doesn't say any
more about this issue than it does about what you have to do to make use
of a third-party library. Nonetheless, it's perfectly feasible on every
platform I've tried. At the very least, deleting the file should be
sufficient on most platforms to remove any possibility of it interfering
with use of the implementation-defined version.
 
P

Philip Potter

jacob said:
In maths N is a subset of R, by the way.
This is not relevant. Floating-point numbers are not real numbers unless
they have infinite precision, which (in comp.lang.c) it's safe to assume
they don't.

In fact when talking about floating-point numbers, you're talking
computing as much as you're talking maths.
 
D

dj3vande

If you mix languages like C and something else, it sort of becomes important
to know what bit-widths C is using.

Every FFI[1] I've ever used (both of them) defines the types it uses at
the interface as "a C int" (or other C type), which makes this problem
go away.

It becomes worse when mixing two implementations of C which cannot agree
what they mean by int or long int, on the same hardware.

If they're both following a consistent ABI that's defined for the
platform they're running on, they shouldn't be disagreeing on the sizes
of their types.
(One possible exception is when you have a single platform with, say, a
32-bit ABI and a 64-bit ABI that can coexist, but the compiler will
still need to know which one it's talking to, and once it knows that it
has enough information to do The Right Thing without further programmer
input.)
If you don't have a consistent ABI, you have bigger problems than
disagreeing on the sizes of your types.

Maybe CPU manufacturers like Intel should keep the word sizes of their
processors secret to encourage good portable programming.

No need. Competent programmers who are writing portable code already
know to ignore, and everybody else will find some way to find it out.


dave

[1] Foreign Function Interface, which in practice usually ends up being
"How to call entry points that are, or at least look like, C
functions, and (if you're lucky) how to make such entry points that
you can be called by"
 
D

dj3vande

CBFalconer said:
Well, if the compiling end uses a C99 compiler, you can't replace
<stdint.h> [1], and thus have to use "stdint.h". Now you are
forbidden to redefine anything defined in <stdint.h>. So, if you
can adhere to all those requirements (and possibly others) and
produce a fully portable "stdint.h", go to it. Show us the result,
please.

--------"stdint.h"
#if __STDC_VERSION__ >= 199901L
#include <stdint.h>
#else
#include "my-stdint.h"
#endif
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

C99 integer types 24
Types 58
Types in C 117
Integer types in embedded systems 22
C99 stdint.h 20
Performance of signed vs unsigned types 84
ansi types 11
using pre-processor to count bits in integer types... 17

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,906
Latest member
SkinfixSkintag

Latest Threads

Top