Fabrizio Iacopetti's Web site |

A few notes concerning C++ integral data types |

You are the visitor n. |

Contents On this page, a few sides of dealing with whole numbers in C++ are described. In particular, a few notes and examples are reported that could help avoiding mistakes while using integers. |

Integral
types in C++ : the int
type C++ allows the use of some predefined types, such as the int type. It is, as its name suggests, a type able to represent integers, or, better, a finite subset of whole numbers. In general, you can use more than one type of integers, for example short int, int, long int, each with its own size, but, take care, it's not always true that short int, int and long int have three different sizes. The lowest and highest integers that can be represented inside a machine, let's say of type int, depend both on the form in which the numbers themselves are represented and, above all, on the amount of memory used to store them. The C++ standard does not say how much memory should be used for the int type, it just says how much memory should be at least used for the int type. For example, the int type is generally stored in four bytes, but this is not always true, on a few implementations it is stored in two bytes. This fact, of course, is against the portability of the code. Concerning integers, when you are going to write a program, you should first of all define the bounds allowed or needed for your integers, and therefore the minimum size required for them; then, you can choose the type you need, for example int or short int, once you know its bounds on your system. To recompile your code with a different compiler and/or on a different platform, and have a working code (at least, concerning the integer type), you should take care that the new compiler uses for your integer types at least the same space than your original compiler. Suppose you write a program that deals with int variables stored by the compiler in four bytes (and, consequently, all operations concerning the int type will be translated into operations involving four data bytes); in this way, you can deal with numbers such as 100000, for example; suppose later you compile your code with another compiler, which stores int numbers in two bytes: I cannot imagine consequences. Numbers like 100000 would be truncated or assigned another value, this could depend on the compiler, anyway something else than 100000. Worth to be mentioned: it has once happened that, with a mistakes someway similar to this, a rocket in which an hardware improvement had been made, without modifying the program adequately, when launched was soon out of control and had to be destroyed. Never allow something like this!! But there is something else to say: in general, other compilers should use the same space used by your original compiler, otherwise some unforeseen effects could occur, expecially when you perform bitwise operations on your integers. |

^ Back to top |

Care with
size and arithmetic The small sample programs on this page have been written and compiled with DevCpp 4.9.8.0 for Win. Please read about it in the "Acknowledge and disclaimers" section. In order to check for the size (in bytes) used by the compiler to store elements of a given type, you could use in your program the sizeof (type) operator. This operator is evaluated at compile-time, not at run-time. If you want to write a program in which, for example, you use integers stored in two bytes, you can check this with the sizeof (int) operator. Let's assume this operator returns 2, so the int type is O.K.. If, on another platform, you compile your program and, there, sizeof(int) returns a value greater than 2, your program may not work; if, instead, you write a program that uses integers stored in four bytes, and then, changing platform, the recompiled program tells you through the sizeof operator that the compiler stores the int type on two bytes, please, never, never, use your original program on that platform. A possibility to allow portability is to use exact-type integers, for example int16_t, int32_t, int64_t, for which you need the stdint library. Please check how to use it. A way to check if your compiler stores integers in the number of bytes you are expecting is, for example, to use in the first lines of your main program something like if (sizeof (int) != 4) {cout << "Int type not stored on four bytes; program aborted\n"; exit (1);} Concerning the size of the integer types, as an instance, on my system DevCpp for Win behaves this way: sizeof (short) = 2 sizeof (short int) = 2 sizeof (int) = 4 sizeof (long int) = 4 sizeof (long long) = 8 sizeof (long long int) = 8 In the following examples, when not otherwise said, we will assume that the int type is stored by the compiler in four bytes. Similar concepts, mutatis mutandis (changing what should be changed), can be applied to integers on less or on more than four bytes (tipically two and eight). Somewhen, in the examples, we will use int on two bytes, as their bounds are quite small and easy to remember. With the int type on two bytes, and two's complement representation, the lowest and the highest int numbers that can be represented are -32768 and +32767. In two's complement representation on N bits, as an example 16, the lowest and the highest whole numbers that can be represented are -(2^(N-1)) and 2^(N-1)-1. Please note that in these lines about representation of numbers the symbol ^ is used to mean "raised to", and is not the C++ XOR operator. Given an integer number x, its two's complement representation X is X=x if 0 <= x <= 2^(N-1) -1 (for example, 0<= x <= 32767) X= 2^N - |x|, if -(2^(N-1)) <= x <= -1 (for example, -32768 <= x <= -1) Conversely, given the representation X in two's complement on N bits of an integer, the corresponding whole number x can be evaluated by x=X if 0 <= X <= 2^(N -1) -1 (for example, 0<= X <= 32767) x= - (2^N - X) , if 2^(N-1) <= X <= (2^N) -1 (for example, 32768 <= X <= 65535) As an example, x= -32768 is represented as X=32768, x= -32769 cannot be represented on 16 bits, x= -32767 is stored as X=32769, x= -1 as X=65535, while x=0 becomes X=0, x=1 becomes X=1, x=32767 becomes X=32767. Conversely, X=65534, as it is greater than 2^(16-1)-1=32767, represents a negative number, exactly -(2^16-X) = -(65536-65534) = -2. After what described just above, you can see that in two's complement representation, mostly or universally adopted to represent whole numbers inside machines, if the first bit (the MSb) is zero the represented number is positive or is zero, if the MSb is one the represented number is negative. Let's now take a look to some problems that can arise with integers, if one does not take enough care. |

^ Back to top |

Increment/decrement,
addition/subtraction Suppose you want to perform a certain operation 40000 times; suppose you write the following C++ program. If you are aware that you need a four byte integer, but you don't check if the int type is really stored in 4 bytes, and your compiler stores int numbers in two bytes, then the program int a=0; for (int a=0; a<40000; a++) {// operations to be done}results in an infinite loop, as after 32767 comes ... -32768, -32767 ... It is possible that the compiler warns you, if in the program the constant value 40000 is assigned to a variable that can't contain it. In this case you must take care to use an integer type stored at least on 4 bytes, for example you could try with the long long int type. Remember this was is example; as another example, in DevCpp 4.9.8.0 for Win an int is on 4 bytes and a long long int is on 8 bytes. Take care: in general, the long int type is stored either in as many bytes as the int type, or in more bytes: check your implementation. Even if you have taken care that your variables fit into the type you have choosen for them, their sum or subraction could generate a result out of bounds allowed for that type, and you cannot recognize it at run-time (unless you try managing overflow, carry and so on). Maybe it could be useful to use "larger" types to store the result of such operations. Similar thing (much more frequent) when you multiply two int; if you are sure that every possible result can fit into an int, you can use an int to store the product, otherwise you should use a "larger" type. With a 4 byte int type, for which bounds are -2147483648 and +2147483647, let's write the following program: int a= 200000; int b=200000; cout << a*b << endl; int c=2000000000; if (a*b < c) {cout << "success\n";}else {cout << "failure\n";}What do you think is the result? The program shows on your screen "success", and a*b is showed to be 1345294336 by the cout operator, instead of 40000000000. You should have used a long long int for c and assign a*b to a long long int d, provided the long long int is on eight bytes. |

^ Back to top |

Absolute
value When you use the int abs(int n) function, due to the dissymetrical representation interval of integers in two's complement, you should take care that n is not the minimum allowed integer; in fact, supposing a short int is on two bytes, the following program does not work. short int asi = 0; asi = -32768; short int aaa=abs(asi); cout << "asi = " << asi << " , aaa = abs(asi) = " << aaa << endl; As abs(-32768) cannot be represented on two bytes, the abs function returns -32768, so you get on the screen asi = -32768 , aaa = abs(asi) = -32768 |

^ Back to top |

Dividing
a number into digits Depending on the implementation, the quotient of the integer division, when the quotient is negative, can be truncated towards zero or towards minus infinite. In the first case, the remainder has the sign of the dividend, in the second has the sign of the divisor. In any case, a = (a/b)*b + a%b Suppose you now want to divide an integer number into its digits, storing them into an array. The following program, if a < 0, may not work; const int n_max_int_digits=32; // int type on 4 bytes int digits[n_max_int_digits] int serv = 0; // ... // serv is assigned a negative value //... // dividing serv into digits for (int i=0; i<n_max_int_digits; i++) { In fact, suppose serv is -342; if serv/10 is -34, and therefore serv %10 is -2, then digits[0]=2, but if serv/10 = -35, serv%10 = +8, and digits[0] is assigned a wrong value. To divide an integer into digits, I instead suggest the following routine: int servizio=0; bool valore_max_is_positivo=false; // valore_max_is_positivo == true if valore_max >= 0 if (valore_max >=0) {valore_max_is_positivo=true;}bool valore_min_is_positivo=false; // valore_min_is_positivo == true if valore_min >= 0 if (valore_min >=0) {valore_min_is_positivo=true;}// dividing valore_min into digits if (valore_min_is_positivo == true) { for (int i=0; i<n_max_cifre_tipo;i++) if (valore_min_is_positivo == false){} { I hope I have been useful to you, I hope soon I can add something else. If you wish, you may mail me for comments and suggestions. |

^ Back to top |

Acknowledgement
and disclaimers The small sample programs on this page have been written and compiled with DevCpp 4.9.8.0 for Win. It is a software free for download, anyway it is subject to different kinds of rights and licenses (Copyright Bloodsheed Software, GNU license, etc.). On these and the preceding lines, quotation of others' work and of licenses needed is not complete; in any case, NO software coming from others' work is included on this page. To use compilers or other programs, please read carefully their license conditions. Being this material at free disposal of whoever desires it, it is forbidden any form of use for commercial purposes. Any form of personal use is certainly allowed. If you wish to use the content of this page not just for personal purposes, it is compulsory to ask a written permit to the author of this article (also by e-mail). The informations on this page are supplied "as they are", without any form of warranty. I don't assume of course any direct or indirect responsibility for direct or indirect damages to things or persons coming from informations got from this article. |

f_iacopetti@libero.it |

Last update 28 July 2004 |