Architectures/ABIs where sizeof(long long) != 8 - c

In x86/amd64 world sizeof(long long) is 8.
Let me quote quite insightful 8 year old mail by Zack Weinberg:
Scott Robert Ladd writes:
On a 64-bit AMD64 architecture, GCC defines long long as 64 bits, the
same as a long.
Given that certain 64-bit instructions (multiply) produce 128-bit
results, doesn't it seem logical the long long be defined as 128 bits?
No, for two reasons:
The choice of 64-bit 'long long' has been written into the ABI of
most LP64-model operating systems; we can't unilaterally change it.
This is actually the correct choice, as it removes the aberration
that makes 'long' not the widest basic integral type. There is
lots and lots of code in the wild written to the assumption that
sizeof(long) >= sizeof(size_t) - this is at least potentially
broken by ABIs where long long is wider than long.
(This was an extremely contentious topic during the development of
C99. As best as I can tell from an outside perspective, 'long long'
was only standardized due to pressure from Microsoft who can't for
some reason implement an LP64 model. Everyone else hated the idea
of making 'long' not necessarily the widest basic integral type.)
Best current practice appears to be to provide an "extended integral
type" __int128. This doesn't have the problems of 'long long' because
it's not a basic integral type (in particular, it cannot be used for
size_t).
zw
long long is widest basic integral type. It's 64-bit long on any non-dead-old architectures/ABIs I know. This allows for going with simple cross-platform (well, at least for many 32/64-bit architectures) typedefs:
typedef char s8;
typedef unsigned char u8;
typedef short s16;
typedef unsigned short u16;
typedef int s32;
typedef unsigned int u32;
typedef long long s64;
typedef unsigned long long u64;
that are nicer than intXX_t, because:
they use same underlying type for 64-bit integers on different platforms
allows avoiding verbose PRId64/PRIu64
(I am well aware that Visual C++ supports %lld/%llu only since 2005)
But how portable this solution is can be expressed by answers to the following question.
What are the architectures/ABIs where sizeof(long long) != 8?
If you cannot provide any recent/modern ones, then go ahead with the old ones, but only if they are still in use.

TI TMS320C55x architecture has CHAR_BIT of 16-bit and long long of 40-bit. Although the 40-bit long long violates ISO, sizeof (long long) is different from 8.
Actually nearly all the C99 implementations with CHAR_BIT > 8 have sizeof (long long) != 8.
TMS320C55x Optimizing C/C++ Compiler User’s Guide (2003)
http://www.ti.com/lit/ug/spru281f/spru281f.pdf

"(This was an extremely contentious topic during the development of C99. As best as I can tell from an outside perspective, 'long long' was only standardized due to pressure from Microsoft who can't for some reason implement an LP64 model. Everyone else hated the idea of making 'long' not necessarily the widest basic integral type.)"
It was contentious among Unix vendors, without reference to Microsoft, which had a lot of I16LP32 code, where the only 32-bit integer was long, so they plausibly did not want to change that.
UNIX & other vendors had ILP32 or like Amdahl, Convex, others ILP32LL64, so needed a 64-bit datatype, just as PDP-11s got to be IP16L32 in mid-1970s to get a 32-bit datatype instead of int X[2].
For the detailed history, here's 2006 article in ACM Queue, later reprinted in 2009 CACM. See especially Table 1.
"The Long Road to 64 Bits
Double, double, toil and trouble"
https://queue.acm.org/detail.cfm?id=1165766
Also, if you read C99, I wrote the rationale for long long.
In the meetings described in the paper, we were split between:
IL32LLP64 - leave long as 32-bit
ILP64 - make ints and longs 64, introduce new type for 32
LP64 - make longs 64, leave ints 32
There were good arguments for each, but the argument was effectively settled by fact that the first two companies shipping 64-bit micros both went LP64.

Your "cross-platform" typedefs are just misguided. The correct ones are
#include <stdint.h>
typedef int8_t s8;
typedef uint8_t u8;
typedef int16_t s16;
...

Related

size_t is unsigned long long under 64 bit system? [duplicate]

I notice that modern C and C++ code seems to use size_t instead of int/unsigned int pretty much everywhere - from parameters for C string functions to the STL. I am curious as to the reason for this and the benefits it brings.
The size_t type is the unsigned integer type that is the result of the sizeof operator (and the offsetof operator), so it is guaranteed to be big enough to contain the size of the biggest object your system can handle (e.g., a static array of 8Gb).
The size_t type may be bigger than, equal to, or smaller than an unsigned int, and your compiler might make assumptions about it for optimization.
You may find more precise information in the C99 standard, section 7.17, a draft of which is available on the Internet in pdf format, or in the C11 standard, section 7.19, also available as a pdf draft.
Classic C (the early dialect of C described by Brian Kernighan and Dennis Ritchie in The C Programming Language, Prentice-Hall, 1978) didn't provide size_t. The C standards committee introduced size_t to eliminate a portability problem
Explained in detail at embedded.com (with a very good example)
In short, size_t is never negative, and it maximizes performance because it's typedef'd to be the unsigned integer type that's big enough -- but not too big -- to represent the size of the largest possible object on the target platform.
Sizes should never be negative, and indeed size_t is an unsigned type. Also, because size_t is unsigned, you can store numbers that are roughly twice as big as in the corresponding signed type, because we can use the sign bit to represent magnitude, like all the other bits in the unsigned integer. When we gain one more bit, we are multiplying the range of numbers we can represents by a factor of about two.
So, you ask, why not just use an unsigned int? It may not be able to hold big enough numbers. In an implementation where unsigned int is 32 bits, the biggest number it can represent is 4294967295. Some processors, such as the IP16L32, can copy objects larger than 4294967295 bytes.
So, you ask, why not use an unsigned long int? It exacts a performance toll on some platforms. Standard C requires that a long occupy at least 32 bits. An IP16L32 platform implements each 32-bit long as a pair of 16-bit words. Almost all 32-bit operators on these platforms require two instructions, if not more, because they work with the 32 bits in two 16-bit chunks. For example, moving a 32-bit long usually requires two machine instructions -- one to move each 16-bit chunk.
Using size_t avoids this performance toll. According to this fantastic article, "Type size_t is a typedef that's an alias for some unsigned integer type, typically unsigned int or unsigned long, but possibly even unsigned long long. Each Standard C implementation is supposed to choose the unsigned integer that's big enough--but no bigger than needed--to represent the size of the largest possible object on the target platform."
The size_t type is the type returned by the sizeof operator. It is an unsigned integer capable of expressing the size in bytes of any memory range supported on the host machine. It is (typically) related to ptrdiff_t in that ptrdiff_t is a signed integer value such that sizeof(ptrdiff_t) and sizeof(size_t) are equal.
When writing C code you should always use size_t whenever dealing with memory ranges.
The int type on the other hand is basically defined as the size of the (signed) integer value that the host machine can use to most efficiently perform integer arithmetic. For example, on many older PC type computers the value sizeof(size_t) would be 4 (bytes) but sizeof(int) would be 2 (byte). 16 bit arithmetic was faster than 32 bit arithmetic, though the CPU could handle a (logical) memory space of up to 4 GiB.
Use the int type only when you care about efficiency as its actual precision depends strongly on both compiler options and machine architecture. In particular the C standard specifies the following invariants: sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) placing no other limitations on the actual representation of the precision available to the programmer for each of these primitive types.
Note: This is NOT the same as in Java (which actually specifies the bit precision for each of the types 'char', 'byte', 'short', 'int' and 'long').
Type size_t must be big enough to store the size of any possible object. Unsigned int doesn't have to satisfy that condition.
For example in 64 bit systems int and unsigned int may be 32 bit wide, but size_t must be big enough to store numbers bigger than 4G
This excerpt from the glibc manual 0.02 may also be relevant when researching the topic:
There is a potential problem with the size_t type and versions of GCC prior to release 2.4. ANSI C requires that size_t always be an unsigned type. For compatibility with existing systems' header files, GCC defines size_t in stddef.h' to be whatever type the system'ssys/types.h' defines it to be. Most Unix systems that define size_t in `sys/types.h', define it to be a signed type. Some code in the library depends on size_t being an unsigned type, and will not work correctly if it is signed.
The GNU C library code which expects size_t to be unsigned is correct. The definition of size_t as a signed type is incorrect. We plan that in version 2.4, GCC will always define size_t as an unsigned type, and the fixincludes' script will massage the system'ssys/types.h' so as not to conflict with this.
In the meantime, we work around this problem by telling GCC explicitly to use an unsigned type for size_t when compiling the GNU C library. `configure' will automatically detect what type GCC uses for size_t arrange to override it if necessary.
If my compiler is set to 32 bit, size_t is nothing other than a typedef for unsigned int. If my compiler is set to 64 bit, size_t is nothing other than a typedef for unsigned long long.
size_t is the size of a pointer.
So in 32 bits or the common ILP32 (integer, long, pointer) model size_t is 32 bits.
and in 64 bits or the common LP64 (long, pointer) model size_t is 64 bits (integers are still 32 bits).
There are other models but these are the ones that g++ use (at least by default)

64 bit operations

I'm writing code for a primality testing function that handles long long int's.Do I have to use special operators for such large numbers?Is there any documentation concerning large number manipulation in C?I'm using the gnu standard library.Thanks.
No, you don't need to do anything special. You handle a long long int just the same way as you would handle a int. Just beware of overflows, as with every native integer type.
If long long ints are supported by your compiler you don't have to do any 'special' kind of stuff. If your processor doesn't support 64-bit types (probably 32-bit-processor then) the compiler will emulate that feature by using sequences of assembly code that breaks up the 64-bit operations into 32-bit ones.
long long is new in C99, though many compilers have supported that as an extension before that.
With gcc a long long is 64 bits, you can use it like any other integer type, nothing special is required.
There's a couple of things to be aware of though, integer constants in the source code needs the LL suffix (or LLU if it's unsigned, e.g. you have to do
long long foo = 123412341234123LL;
and not
long long foo = 123412341234123;
Similarly, for outputting a long long with the printf family, you have to use the conversion specifier "%lld" instead of "%d" or "%ld" (or "%llu" if it's unsigned), e.g.
printf("foo = %lld",foo);
There's some docs about long long in gcc here
If the compiler supports long long int, it works with standard operators.
By the way, long long int is 128 bits on 64-bit unices (where long alone is 64 bits). Use int64_t from <stdint.h> if you need 64-bits on all platforms. This does not apply to 64-bit windows, where long is still 32 bits and long long is 64 bits.
If you are just handling long long int you don't need anything special as long as your compiler supports it. Take care of overflows while adding and multiplying two long long ints
For handling very large numbers(range much greater than that of long long int) have a look at GNU MP BigNum Library
Have a look at the GMP library: http://gmplib.org/

Is int in C Always 32-bit?

This is related to following question,
How to Declare a 32-bit Integer in C
Several people mentioned int is always 32-bit on most platforms. I am curious if this is true.
Do you know any modern platforms with int of a different size? Ignore dinosaur platforms with 8-bit or 16-bit architectures.
NOTE: I already know how to declare a 32-bit integer from the other question. This one is more like a survey to find out which platforms (CPU/OS/Compiler) supporting integers with other sizes.
As several people have stated, there are no guarantees that an 'int' will be 32 bits, if you want to use variables of a specific size, particularly when writing code that involves bit manipulations, you should use the 'Standard Integer Types' mandated by the c99 specification.
int8_t
uint8_t
int32_t
uint32_t
etc...
they are generally of the form [u]intN_t, where the 'u' specifies that you want an unsigned quantity, and N is the number of bits
the correct typedefs for these should be available in stdint.h on whichever platform you are compiling for, using these allows you to write nice, portable code :-)
"is always 32-bit on most platforms" - what's wrong with that snippet? :-)
The C standard does not mandate the sizes of many of its integral types. It does mandate relative sizes, for example, sizeof(int) >= sizeof(short) and so on. It also mandates minimum ranges but allows for multiple encoding schemes (two's complement, ones' complement, and sign/magnitude).
If you want a specific sized variable, you need to use one suitable for the platform you're running on, such as the use of #ifdef's, something like:
#ifdef LONG_IS_32BITS
typedef long int32;
#else
#ifdef INT_IS_32BITS
typedef int int32;
#else
#error No 32-bit data type available
#endif
#endif
Alternatively, C99 and above allows for exact width integer types intN_t and uintN_t:
The typedef name intN_t designates a signed integer type with width N, no padding bits, and a two's complement representation. Thus, int8_t denotes a signed integer type with a width of exactly 8 bits.
The typedef name uintN_t designates an unsigned integer type with width N. Thus, uint24_t denotes an unsigned integer type with a width of exactly 24 bits.
These types are optional. However, if an implementation provides integer types with widths of 8, 16, 32, or 64 bits, no padding bits, and (for the signed types) that have a two's complement representation, it shall define the corresponding typedef names.
At this moment in time, most desktop and server platforms use 32-bit integers, and even many embedded platforms (think handheld ARM or x86) use 32-bit ints. To get to a 16-bit int you have to get very small indeed: think "Berkeley mote" or some of the smaller Atmel Atmega chips. But they are out there.
No. Small embedded systems use 16 bit integers.
It vastly depends on your compiler. Some compile them as 64-bit on 64-bit machines, some compile them as 32-bit. Embedded systems are their own little special ball of wax.
Best thing you can do to check:
printf("%d\n", sizeof(int));
Note that sizeof will print out bytes. Do sizeof(int)*CHAR_BIT to get bits.
Code to print the number of bits for various types:
#include <limits.h>
#include <stdio.h>
int main(void) {
printf("short is %d bits\n", CHAR_BIT * sizeof( short ) );
printf("int is %d bits\n", CHAR_BIT * sizeof( int ) );
printf("long is %d bits\n", CHAR_BIT * sizeof( long ) );
printf("long long is %d bits\n", CHAR_BIT * sizeof(long long) );
return 0;
}
TI are still selling OMAP boards with the C55x DSPs on them, primarily used for video decoding. I believe the supplied compiler for this has a 16 bit int. It is hardly dinosaur (the Nokia 770 was released in 2005), although you can get 32 bit DSPs.
Most code you write, you can safely assume it won't ever be run on a DSP. But perhaps not all.
Well, most ARM-based processors can run Thumb code, which is a 16-bit mode. That includes the yet-only-rumored Android notebooks and the bleeding-edge smartphones.
Also, some graphing calculators use 8-bit processors, and I'd call those fairly modern as well.
If you are also interested in the actual Max/Min Value instead of the number of bits, limits.h contains pretty much everything you want to know.

What kind of data type is "long long"?

I don't know this type. Is that the biggest one from all? I think it is an integer type, right? Or is it a floating point thing? Bigger than double?
According to C99 standard, long long is an integer type which is at least 64-bit wide. There are two integer 64-bit types specified: long long int and unsigned long long int
So, yes, this is the biggest integer type specified by C language standard (C99 version).
There is also long double type specified by C99. It's an extended precision floating point numeric data type long for 80-bits on most popular x86-based platforms and implementations of C language.
The short and simple is that a long long is an int that is at least 64 bits wide. The rationale for this is here. Basically, it is a response to 64 bit architecture and backwards compatibility. And the name long long was deemed the least bad of all possibilities by the standards committee.

when would you use uint_least16_t

I'm looking at stdint.h and given that it has uint16_t and uint_fast16_t, what is the use for uint_least16_t what might you want that couldn't be done equally well with one of the other two?
Say you're working on a compiler with:
unsigned char is 8 bits
unsigned short is 32 bits
unsigned int is 64 bits
And unsigned int is the 'fastest'. On that platform:
uint16_t would not be available
uint_least16_t would be a 32 bit value
uint_fast16_t would be a 64 bit value
A bit arcane, but that's what it's for.
How useful they are is another story - I see the exact size variants all the time. That's what people want. The 'least' and 'fast' versions I've seen used pretty close to never (it's possible that it was only in example code - I'm really not sure).
Ah, the link Patrick posted includes this "The typedef name uint_leastN_t designates an unsigned integer type with a width of at least N, such that no unsigned integer type with lesser size has at least the specified width."
So my current understanding is:
uint_least16_t the smallest thing that is capable of holding a uint16
uint_fast16_t the fastest thing that is capable of holding a uint16
uint16_t exactly a uint16, unfortunately may not be available on all platforms, on any platform where is is available uint_least16_t will refer to it. So if it were guaranteed to exist on all platforms we wouldn't need uint_least16_t at all.
It's part of the c standard. It doesn't need a good use case. :P
See this page and look for the section titled "Minimum-width integer types".

Resources