Run codes as you go...
Use Mozilla Firefox for better experience
Float and Double
In programming, particularly, C++, float and double are both used for the representation of fractional/decimal numbers. In other words, the difference between float and double arises from the fact with how much precision (the degree/quality of being exact and accurate) they can store the numbers. For example, one may decide to store a mathematical constant \(\pi\) as 3.14 or 3.14159. A variable able to store \(\pi\) as 3.14159 provides more precision than the one able to hold \(\pi\) as 3.14. Float can accurately store about 7 - 8 digits in the fractional part while double can accurately store about 15 - 16 digits in the fractional part. These fractions/decimals can amount to a huge difference in large calculations and significantly affect results.
Single precision floating point and Double precision floating point
Precision in computer science is the use of a higher level of detail to express the same number. Instead of using decimal points to represent the number, we use bits, or binary digits to express such a number. If we think of a number such as $\pi$ expressed in traditional scientific notation \(3.14 \times 10^{0}\), computers store such a number in binary as a floating-point, a series of ones and zeroes that represent a number and its corresponding exponent. According to the IEEE 754 standard, floating-point numbers are commonly represented in two ways, single and double precision formats. In single-precision format, each number takes up to 32 bits, also called binary32 (this means a number occupies 32 bits in computer memory), and double-precision format uses up to 64 bits (binary64 -- it occupies 64 bits in computer memory). The range of numbers in single-precision is \(2^{(-126)}\) to \(2^{(+127)}\), and the range of numbers in double precision is \(2^{(-1022)}\) to \(2^{(+1023)}\).
In a single-precision, that is a 32-bit format, one bit (signed bit) is used to tell whether the number is positive or negative. Eight bits are reserved for the exponent, a power to base 2 (because it’s binary). The remaining 23 bits are used to represent the fractional digits/bits that make up the number, called the mantissa. Double precision instead uses 11 bits for the exponent and 52 bits for the mantissa. Double floating-point precision expands the range and size of numbers it can represent, hence representing numbers in higher details than single precision. See also (here) for half-precision, multi-precision, and mixed precision.
\[
\begin{array}{|c|c|c|c|c|c|}
\hline
\text{Precision} &\text{Base}& \text{Sign} & \text{Exponent} & \text{Mantissa} & \text{Significand}\\
\hline
\text{Single Precision}& 2 & 1 & 8 & 23 & 23 + 1 \\
\hline
\text{Double Precision}& 2 & 1 & 11 & 52 & 52 + 1 \\
\hline
\end{array}
\]
Note that, the higher the precision level a computer uses, the more computational resources, the more RAM, data transfer, memory storage, and so on it requires. High precision is thus computationally more costly and consumes more power as well.
Summary
Single precision
![Image](https://media.geeksforgeeks.org/wp-content/uploads/20200427104522/single-precision.png)
Ref: Image by geeksforgeeks
The IEEE single-precision floating-point standard representation requires a 32-bit word, which may be represented as numbered from 0 to 31, left to right:
- The first bit is the sign bit, S,
- the next eight bits are the exponent bits, 'E', and
- the final 23 bits are the mantissa 'M' (also called fraction or coefficient):
- S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM
- 0 1 8 9 31
Double precision
![Image](https://media.geeksforgeeks.org/wp-content/uploads/20200427104545/double-precision.png)
Ref: Image by geeksforgeeks
The IEEE double-precision floating-point standard representation requires a 64-bit word, which may be represented as numbered from 0 to 63, left to right:
- The first bit is the sign bit, S,
- the next eleven bits are the exponent bits, 'E', and
- the final 52 bits are the mantissa 'M':
- S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
- 0 1 11 12 63
Usually, the sign bits are the last, being the 31st bit and 63rd bit, in single and double precision, respectively. So the correct representation of a single-precision using a word of 32 bit, and double-precision using a word of 64 bit, would respectively look as follows:
- 1 bit for the sign, S,
- 8 bits for the exponent, 'E',
- 24 bits for mantissa (note that only 23 are represented below):
- S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM
- 31 30 23 22 0
and,
- 1 bit for the sign, S,
- 11 bits for the exponent, 'E',
- 53 bits for mantissa (note that only 52 are represented below):
- S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
- 63 62 52 51 0
Usually, mantissa has, in both cases, an extra bit of information (one bit) compared to its representation above. A mantissa is a number represented without all its non-significative 0 (hence the term significand). For example,
- \(0.00135\) which becomes \(1.35 \times 10^{−3}\) in scientific notation, has three significant figures: 1, 3, and 5. Leading zeros -- zeros before non-zero numbers are not significant.
- \(0.000122300 = 1.22300 \times 10^{-4}\) in scientific notation and has six significant figures. See more here.
Single and double precision do not originate from the fact that the double of \(32\) is \(64\), but rather the precision indicates the number of decimal digits that are correct, i.e. without any kind of representation error or approximation. In other words, precision indicates the number of decimal digits one can safely use. Therefore, the number of decimal digits which can be safely used, can respectively be estimated for single and double precision as follows:
- Single precision: \({\text log}_{10}(2^{24})\), which is about \(7\sim 8\) decimal digits.
- Double precision: \({\text log}_{10}(2^{53})\), which is about \(15\sim 16\) decimal digits.
Scientific Notation
Let us convert \(3.14\) to scientific notation under base 2.
Step 1: Divide the whole part, i.e., \(3\) and the resulting output throughout by 2 and record the remainder in each step. Stop when the division output is \(0\).
\[
\begin{array}{|c|c|c|}
\hline
\text{Divisor} &\text{Dividend}& \text{Remainder}\\
\hline
2 & 3 & \\
\hline
2 & 1 & 1 \\
2 & 0& 1\\
\hline
\end{array}
\]
Here, the binary number for \( 3\) is the sequence of ones in the "Remainder" column above, written from down to the top. Thus, \( 3 \equiv 11 \) in binary form.
Step 2: Convert the fractional part, \(0.14\) into binary form by multiplying it by \(2\) and recording the value of the output before the decimal point. Repeatedly multiply the resulting decimals/fractions by \(2\) and record the values before the decimal point:
\[
\begin{array}{|c|c|c|}
\hline
\text{Multiply by 2} &\text{Result}& \text{Value before decimal point}\\
\hline
0.14 \times 2 & 0.28 & 0\\
\hline
0.28 \times 2 & 0.56 & 0\\
\hline
0.56 \times 2 & 1.12& 1\\
\hline
0.12 \times 2 & 0.24 & 0\\
\hline
0.24 \times 2 & 0.48 & 0\\
\hline
0.48 \times 2 & 0.96 & 0\\
\hline
0.96 \times 2 & 1.92 & 1\\
\hline
0.92 \times 2 & 1.84 & 1\\
\hline
0.84 \times 2 & 1.68 & 1\\
\hline
0.68 \times 2 & 1.36 & 1\\
\hline
0.36 \times 2 & 0.72 & 0\\
\hline
0.72 \times 2 & 1.44 & 1\\
\hline
0.44 \times 2 & 0.88 & 0\\
\hline
0.88 \times 2 & 1.76 & 1\\
\hline
\dots & \dots & \dots \\
\hline
\end{array}
\]
From the table, \(0.14 \equiv 0.00100011110101 \) in binary form. Thus, in binary form, \(3.14 \equiv 11.00100011110101\).
In scientific notation (base 2), \(3.14 \equiv 11.00100011110101 = 1.100100011110101 \dots \times 2^{1} \).
Single Precision Floating Point Representation
Let us convert the number \(263.3\) into a single-precision floating-point representation.
Step 1: Convert the whole part, \(263\) into a binary system: Divide \(263\) and the resulting output throughout by 2 and record the remainder in each step. Stop when the division output is \(0\).
\[
\begin{array}{|c|c|c|}
\hline
\text{Divisor} &\text{Dividend}& \text{Remainder}\\
\hline
2 & 263 & \\
\hline
2 & 131 & 1 \\
\hline
2 & 65 & 1\\
\hline
2 & 32 & 1\\
\hline
2 & 16 & 0\\
\hline
2 & 8 & 0\\
\hline
2 & 4 & 0\\
\hline
2 & 2 & 0\\
\hline
2 & 1 & 0\\
\hline
& 0 & 1\\
\hline
\end{array}
\]
The binary number is a sequence of ones and zeroes in the "Remainder" column above, written from down to the top. Thus \(263\) in binary form is \(100000111\).
To convert \(0.3\) into binary form, we multiply it by \(2\) and record the value of the output before the decimal point (this is either \(0\) or \(1\)). We repeatedly multiply the resulting decimals/fractions by \(2\) and record the values before the decimal point:
\[
\begin{array}{|c|c|c|}
\hline
\text{Multiply by 2} &\text{Result}& \text{Value before decimal point}\\
\hline
0.3 \times 2 & 0.6 & 0\\
\hline
0.6 \times 2 & 1.2 & 1\\
\hline
\hline
0.2 \times 2 & 0.4 & 0\\
\hline
0.4 \times 2 & 0.8 & 0\\
\hline
0.8 \times 2 & 1.6 & 1\\
\hline
0.6 \times 2 & 1.2 & 1\\
\hline
\hline
\dots & \dots & \dots \\
\hline
\end{array}
\]
From the table above, we can write \(0.3\) in binary form as \(.01001100110011\dots\).
Therefore, \(263.3\) in binary form is \(100000111.01001100110011\dots\).
In scientific notation (we shift the decimal point \(8\) positions to the left and multiply the result by \(2\) raised to some power):
\[ 1.00000111010011001100110\dots \times 2^{8} \].
Recall the single-precision floating-point (binary32) format
- S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM
- 0 1 8 9 31
and consider the following:
- If the number being considered is positive, the sign bit, \(S = 0\).
- If the number being considered is negative, the sign bit, \(S = 1\).
- We need to take into account the exponent bias. For a single precision floating point (IEEE 754 format), the bias is given by \(2^{(\text{Number of exponent bits} - 1)} - 1 = 2^{(8-1)} - 1 = 2^7 - 1 = 127\).
"We have 8 bits to represent the exponent. We can represent \(2^8 = 256\) numbers with \(8\) bits. The bias is considered to be half of it minus 1 so that we can represent both positive and negative exponents." See comments here.
\(\\ \) - We need to add the exponent in the standard notation (the power of 2 -- which can be positive or negative) to the bias obtained above. Adding bias to the exponent, we have \(127 + 8 = 135\).
Now, let's convert the resulting \(135 \) into binary format:
\[
\begin{array}{|c|c|c|}
\hline
\text{Divisor} &\text{Dividend}& \text{Remainder}\\
\hline
2 & 135 & \\
\hline
2 & 67 & 1\\
\hline
2 & 33 & 1\\
\hline
2 & 16 & 1\\
\hline
2 & 8 & 0\\
\hline
2 & 4 & 0\\
\hline
2 & 2 & 0\\
\hline
2& 1 & 0\\
\hline
& 0 & 1\\
\hline
\end{array}
\]
Therefore, \(135\) in binary form is \(10000111\).
Let's now write our number, \(263.3\) in binary32 following the convention,
\[
\begin{array}{|c|c|c|}
\hline
\text{S} &\text{E...E}& \text{M...M}\\
\hline
1 \ \text{Sign bit} & 8 \ \text{Exponent bits} & 23 \ \text{Fraction/Mantissa bits}\\
\hline
0 & 10000111& 00000111010011001100110 \\
\hline
\end{array}
\]
Thus, \(263.3\) in IEEE754 single-precision floating-point representation is \(01000011100000111010011001100110\).
Here, the fractional bits (mantissa) are the bits that follow the decimal point in scientific notation, \( 1.00000111010011001100110\dots \times 2^{8} \), that is \[ 00000111010011001100110\].
Ref:
Convert IEEE 754 Single-Precision (32-bit) Floating-Point Representation to Equivalent Decimal Number
We can reverse the binary number (binary32) into its equivalent decimal number. Let us find the decimal equivalent for the following binary32:
- 0 10000101 11110110000000000000000
Sign bit
From the given binary32, we see that the signed bit = 0, so the decimal equivalent number is positive. This is because, our decimal equivalent is calculated from the binary representation:
\begin{align}
(-1)^{S}\times(1+M)\times 2^{E} = (-1)^{S}\times(1.M)\times 2^{E},
\end{align}
where \(S\) is the signed bit, and \(M\) is the mantissa.
Exponent bits:
In scientific notation the binary32 takes the form \(1.M \times 2^{E} \), where \(M\) is the mantissa and \(E\) is the exponent.
We convert the exponent bits from binary to decimal as follows:
\begin{align}\nonumber
\begin{cases}
(10000101)_{2} & = & 1 \times 2^0 + 0 \times 2^1 + 1 \times 2^2 + 0 \times 2^3 + 0 \times 2^4 + 0 \times 2^5 + 0 \times 2^6 + 1 \times 2^7\\
& & \text{(1, 0, 1, ... are multiplied from right to left)}\\
& = & 1 + 0 + 4 + 0 + 0 + 0 + 0 + 128 \\
&=& 133.
\end{cases}
\end{align}
The exponential bias for binary32 = \(2^{8 -1} - 1 = 127 \), and so any exponent bits that represent a number less than \(127\) would result in a negative exponent \(E\) (refer the scientific notation section). Since \(133>127\), we have \(E = 133 - 127 = 6 \) (here, exponent bit was added to the bias).
Therefore, in scientific notation our number now takes the form
\begin{align}\label{eq2}
\begin{split}
1.M\times 2^{6} & = 1.11110110000000000000000 \times 2^{6}\\
&= 1111101.10000000000000000.
\end{split}
\end{align}
Convert the whole part into decimal number:
We convert the whole part (binary) from Equation (\ref{eq2}) into decimal number:
\begin{align}\nonumber
\begin{cases}
(1111101)_{2} & = & 1 \times 2^0 + 0 \times 2^1 + 1 \times 2^2 + 1 \times 2^3 + 01\times 2^4 + 1 \times 2^5 + 1 \times 2^6 \\
& & \text{(1, 0, 1, ... are multiplied from right to left)}\\
& = & 1 + 0 + 4 + 8 + 16 + 32 + 64\\
&=& 125.
\end{cases}
\end{align}
Convert the fractional part into decimal
We convert the fractional part (binary) from Equation (\ref{eq2}) into decimal number:
\begin{align}\nonumber
\begin{cases}
(0.100\dots)_{2} &=& 1 \times 2^{-1} + 0 \times 2^{-2} + \dots \\
&=& 0.5 + 0 + 0 + \dots\\
&=& 0.5.\\
\end{cases}
\end{align}
Finally, the decimal equivalent number is given by the sum of the whole part and the fractional part, thus our decimal number is \(125.5\).
After seeing the example above, we can now easily reverse from binary32,
- 0 10000111 00000111010011001100110
to \(263.3\) as follows:
Calculate the exponent from the exponent bits (from base 2 to base 10):
\begin{align}
\begin{split}
(10000111)_{2} & = 1\times 2^{0} + 1\times 2^{1} + 1\times 2^{2} + 0 \times 2^{3} + 0 \times 2^{4} + 0 \times 2^{5} + 0 \times 2^{6} + 1 \times 2^{7}\\
& = 1 + 2 + 4 + 0 + \dots + 0 + 128\\
& = 135\\
\implies & E = 135 - 127 = 8.
\end{split}
\end{align}
From scientific notation in binary form, our number takes the form
\begin{align}
\begin{split}
1.M \times 2^{8} & = 100000111.010011001100110\\
& = 2^{0} + 2^{1} +2^{2}+0+0+0+0+0+2^{8} \ \text{(whole part)}\\
& + 0\times 2^{-1} + 1\times 2^{-2} + 0\times 2^{-3} + 0\times 2^{-4} + 1\times 2^{-5} \\
& + 1\times 2^{-6} + 0\times 2^{-7} + 0\times 2^{-8} + 1\times 2^{-9} + 1\times 2^{-10} \\
& + 0\times 2^{-11} + 0\times 2^{-12} + 1\times 2^{-13} + 1\times 2^{-14} + 0 \ \text{(fractional part)} \\
& = (1 + 2 + 4 + 256) \\
& + \frac{1}{4} + \frac{1}{32} + \frac{1}{64} + \frac{1}{512} + \frac{1}{1024} + \frac{1}{8192} + \frac{1}{16384}\\
\\
& = 263 + 0.29998779296875\\
& \approx 263.3.
\end{split}
\end{align}
Input and Output
Consider the program below:
- #include<iostream>
- int main ()
- {
- char i1 = 25;
- std::cout<<"The answers to the Ultimate Question of Life, \n"
- <<"the Universe, and Everything are:"
- <<std::endl<< 8*9 <<std::endl;
- std::cout<< i1*2 <<std::endl;
- std::cout<< "and"<<std::endl;
- std::cout<< 'a' + 12 <<std::endl;
- return 0;
- }
Input and output are not part of the core language but are provided by the library. They must be included explicitly; otherwise, we cannot read or write.
The standard I/O (input - output) has a stream model and is therefore named <iostream>. To enable its functionality, we include <iostream> in the first line
Every C ++ program starts by calling the function main. It does return an integer value where 0 represents a successful termination.
Braces {} denote a block/group of code (also called a compound statement).
std::cout and std::endl are defined in <iostream> . The former is an output stream that prints text on the screen. std::endl terminates a line. We can also go to a new line with the special character \n .
The operator << can be used to pass objects to an output stream such as std::cout for performing an output operation.
std:: denotes that the type or function is used from the standard Namespace. Namespaces help us to organize our names and to deal with naming conflicts.
String constants (more precisely literals) are enclosed in double quotes.
The expression 8 * 9 is evaluated and passed as an integer to std::cout. In C ++, every expression has a type. Sometimes, we as programmers have to declare the type explicitly and other times the compiler can deduce it for us. 6 and 7 are literal constants of type int and accordingly their product is int as well.
Variables
C ++ is a strongly typed language (in contrast to many scripting languages). This means that every variable has a type and this type never changes. A variable is declared by a statement beginning with a type followed by a variable name with optional initialization, for example:
- int i1 = 2;
- int i2 = 3, i3 = 5
- char c1 = 'a', c2 = 25;
- float pi = 3.14159;
- bool bl = i1<pi, //-> true
- sad = true;
Basic types (Variables) (also called Intrinsic Types)
All variables use data-type during declaration to restrict the type of data to be stored. Therefore, we can say that data types are used to tell the variables the type of data it can store. Whenever a variable is defined in C++, the compiler allocates some memory for that variable based on the data-type with which it is declared. Every data type requires a different amount of memory.
\[
\begin{array}{|c|c|}
\hline
\text{Name/Keyword} & \text{Semantics (Meaning)} \\
\hline
\text{char} & \text{8-bit signed data item. Letter and very short integer number} \\
\hline
\text{short} & \text{Rather short integer number} \\
\hline
\text{int} & \text{Regular integer number. Its size is system dependent}\\
\hline
\text{long} & \text{Long integer number} \\
\hline
\text{long long} & \text{Very long integer number}\\
\hline
\text{signed} & \text{Signed versions of all the former}\\
\hline
\text{unsigned} & \text{Unsigned versions of all the former except signed}\\
\hline
\text{float} & \text{Single-precision floating-point number. 32-bit IEEE floating point number}\\
\hline
\text{double} & \text{Double-precision floating-point number. 64-bit IEEE floating-point number} \\
\hline
\text{long double} & \text{long floating-point number} \\
\hline
\text{void} & \text{Valueless or void}\\
\hline
\text{wchar_t} & \text{Unicode character accepted only for 32-bit type libraries}\\
\hline
\end{array}\]
Note:
"Unicode is a universal character encoding standard. It defines the way individual characters are represented in text files, web pages, and other types of documents.
Unlike ASCII characters, which were designed to represent only basic English characters, Unicode was designed to support characters from all languages around the world. The standard ASCII character set only supports 128 characters, while Unicode can support roughly 1,000,000 characters. While ASCII only uses one byte to represent each character, Unicode supports up to 4 bytes for each character" See more >>>.
The first five types are integer numbers of non-decreasing length. For instance, int is at least as long as short ; i.e., it is usually but not necessarily longer. The exact length of each type is a platform (implementation)-dependent; e.g., int could be 16, 32, or 64 bits. All these types
can be qualified as signed or unsigned. signed has no effect on integer numbers (except char ) since they are signed by default.
When we declare an integer type as unsigned, we will have no negative values but twice as many positive ones (plus one when we consider zero as neither positive nor negative). A signed integer is a 32-bit datum that encodes an integer in the range from -2147483648 to 2147483647. An unsigned integer is a 32-bit datum that encodes a non-negative integer in the range from 0 to 4294967295, see Ref. Unsigned integers (sometimes called "units") are just like integers (whole numbers) but have the property that they don't have a + or - sign associated with them. Thus they are always non-negative (zero or positive). signed and unsigned can be considered adjectives for the nouns short, long, char, int, with int as the default noun when the adjective only is declared.
The non-decreasing length property also applies in the same manner to floating-point numbers. float is shorter than or equally as long as double, which in turn is shorter than or equally as long as long double. Typical sizes are 32 bits for float, 64 bits for double, and 80 bits for long double.
short, long, signed, unsigned are called datatype modifiers and are used with the built-in data types to modify the length of data that a particular data type can hold. If we ignore access modifiers, the intrinsic/basic types, reduces to what is mostly agreed to be Primitive Data Types -- fundamental, or built-in, types:
\[
\begin{array}{|c|c|}
\hline
\text{Data Type} & \text{Keyword} \\
\hline
\text{Integer} & \text{int}\\
\hline
\text{Character} & \text{char}\\
\hline
\text{Boolean} & \text{bool}\\
\hline
\text{Floating Point} & \text{float}\\
\hline
\text{Double Floating Point} & \text{double}\\
\hline
\text{Valueles or Void} & \text{void}\\
\hline
\text{Wide Character} & \text{wchar_t}\\
\hline
\end{array}\]
These data types are built-in or predefined.
The type char can be used in two ways: for letters and for short numbers, and it almost always has a length of 8 bits. Thus, we can either
represent values from -128 to 127 signed) in or from 0 to 255 (unsigned) and safely perform all numeric operations on them that are available for integers. When neither signed nor unsigned is declared, it depends on the implementation of the compiler which one is used.
Note that, the C ++ compiler considers 'a' as the character "a" (it has type char) and interprets "a" as the string containing “a” and a binary 0 as termination.
Wide character (wchar_t) data type is also a character data type but has a size greater than the normal 8-bit data type, and is generally 2 or 4 bytes long.
Boolean (bool) data types are used for storing boolean or logical values. A boolean variable can store either true or false.
void is only allowed as a return type for a function, or in a function parameter list to indicate no arguments.
We summarize using the C++ code below the modified size and range of built-in datat ypes when combined with the type modifiers:
- //C++ program to determine sizes of data types
- #include<iostream>
- using namespace std;
- int main()
- {
- cout<< "Size of char: " <<sizeof(char)<< " byte" << endl;
- cout << "Size of signed char: " <<sizeof(signed char)<< " byte" << endl;
- cout<< "Size of unsigned char: " <<sizeof(unsigned char)<< " byte" << endl;
- cout<< "Size of short int: " <<sizeof(short int)<< " bytes" << endl;
- cout<< "Size of unsigned short int: " <<sizeof(unsigned short int)<< " bytes" << endl;
- cout<< "Size of int: " <<sizeof(int)<< " bytes" << endl;
- cout<< "Size of signed: " <<sizeof(signed)<< " bytes" << endl; // int is the default noun
- cout<< "Size of unsigned: " <<sizeof(unsigned)<< " bytes" << endl; // int is the default noun
- cout<< "Size of unsigned int: " <<sizeof(unsigned int)<< " bytes" << endl;
- cout<< "Size of long int: " <<sizeof(long int)<< " bytes" << endl;
- cout<< "Size of signed long int: " <<sizeof(signed long int)<< " bytes" << endl;
- cout<< "Size of unsigned long int: " <<sizeof(unsigned long int)<< " bytes" << endl;
- cout<< "Size of long long int: " <<sizeof(long long int)<< " bytes" << endl;
- cout<< "Size of unsigned long long int: " <<sizeof(unsigned long long int)<< " bytes" << endl;
- cout<< "Size of float: " <<sizeof(float)<< " bytes" <<endl;
- cout<< "Size of double: " << sizeof(double)
- << " bytes" << endl;
- cout<< "Size of long double: " <<sizeof(long double)<< " bytes" << endl;
- cout << "Size of wchar_t: "<< sizeof(wchar_t)<< " bytes" <<endl;
- return 0;
- }
These values (bytes) may vary from compiler to compiler, here we present the output of the code above as determined using g++ version 7.5.0 under Ubuntu Linux 18.04 LTS:
- Size of char: 1 byte
- Size of signed char: 1 byte
- Size of unsigned char: 1 byte
- Size of short int: 2 bytes
- Size of unsigned short int: 2 bytes
- Size of int: 4 bytes
- Size of signed: 4 bytes
- Size of signed: 4 bytes
- Size of unsigned int: 4 bytes
- Size of long int: 8 bytes
- Size of signed long int: 8 bytes
- Size of unsigned long int: 8 bytes
- Size of long long int: 8 bytes
- Size of unsigned long long int: 8 bytes
- Size of float: 4 bytes
- Size of double: 8 bytes
- Size of long double: 16 bytes
- Size of wchar_t: 4 bytes
auto
C ++ 11 can automatically deduce the type of a variable, for example,
- auto i4 = i3 + 7;
Here, the type of i4 is automatically determined and is the same as that of i3 + 7, which is int. The type remains the same, and whatever is assigned to i4 afterward will be converted to int. auto is very useful in advanced programming.
Constants
Syntactically, constants are like special variables in C ++ with the additional attribute of constancy. Since they cannot be changed, their values must be set at the point of declaration. Constants can be used wherever variables are allowed provided they are not
modified. They can also be used as arguments of types. In the code below,
- #include<iostream>
- int main ()
- {
- const int ci1 = 5;
- const int ci3; //error: uninitialized const ‘ci3’
- const float pi = 3.14359;
- const char cc = 'a';
- const bool bl = ci1 > pi ;
- return 0;
- }
the compiler will raise an error: "uninitialized const ci3", because the second constant declaration, i.e, "const int ci3;" violates the rule, that is constant values need to be initialized during declaration.
Temporaries and Literals
Data resulting from computation or manipulation with C++ may represent many different things, from simple numbers and strings, to
images and multimedia files, to abstract numerical simulations and their solutions.
C++ knows three different categories of data:
- Variables are names for memory locations where data is stored, e.g., int i1 = 5; referring to an integer value in memory.
- Temporaries represent values that are not necessarily stored in memory, e.g., intermediate values in compound expressions and function return
values. And - Literals are values that are explicitly mentioned in the source code, e.g., the number 5 above, or the string "Hello World!".
Every number with a dot or an exponent, for example, \(2.14 \times 10^{12} \) is considered a double. Integer literals are treated as int, long, unsigned or unsigned long depending on the number of digits. Literals of other types can also be qualified by explicitly adding a suffix to indicate a type as shown in the table below:
\[
\begin{array}{|c|c|}
\hline
\text{Data Type} & \text{Keyword} \\
\hline
2 & \text{int} \\
\hline
\text{2u} & \text{unsigned} \\
\hline
\text{2l} & \text{long} \\
\hline
\text{2ul} & \text{unsigned long}\\
\hline
\text{2.0} & \text{double}\\
\hline
\text{2.0f} & \text{float}\\
\hline
\text{2.0l} & \text{long double}\\
\hline
\end{array}\]
In most cases, it is not necessary to declare the type of literals explicitly since the implicit conversion between built-in numeric types usually sets the values at the programmer’s expectation. But, here are three major reasons why we should explicitly specify the types of
literals.
- Availability: The standard library provides a type for complex numbers where the types for the real and imaginary parts can be parameterized by the user (see below). In this case, operations are only possible between the type itself and the underlying real
type (arguments are not converted here). As a result, it is not possible to multiply z with an int or double but with float:
\(\\ \)- #include <complex>
- int main ()
- {
- std::complex<float> z(1.3 , 2.4), z2;
- z2 = 2.0f*z;
- // Not possible
- z2 = 2*z; /* note: mismatched types ‘const std::complex<_Tp>’ and ‘int’ z2 = 2*z; */
- z2 = 2.0*z; /* note: mismatched types ‘const std::complex<_Tp>’ and ‘double’ z2 = 2.0*z; */
- // Works
- z2 = 2.0f*z; //float*complex<float>
- return 0;
- }
- Ambiguity: When a function is overloaded for different argument types, an argument like 0 might be ambiguous whereas a unique match may exist for a qualified argument like 0u.
- Accuracy: The accuracy issue comes up when we work with long double. Since the non-qualified literal is a double, we might lose digits if we do not explictly assign it to a long double variable:
- long double d1 = 0.3333333333333333333; // May lose digits
- long double d2 = 0.3333333333333333333l; // Accurate
Non-decimal numbers
Octal literals/numbers
Integer literals starting with a zero are interpreted as octal numbers, for example:
- #include<iostream>
- int main ()
- {
- int o1 = 042; // int o1 = 34;
- int o2 = 0106; // int o2 = 70;
- int o3 = 031; // int 03 = 25;
- int o4 = 020; // int 04 = 16;
- int o5 = 084; //error: invalid digit "8" in octal constant int o5 = 084;
- std::cout<<"Octal numbers o1 is: "<<o1<<std::endl;
- std::cout<<"Octal numbers o2 is: "<<o2<<std::endl;
- std::cout<<"Octal numbers o3 is: "<<o3<<std::endl;
- std::cout<<"Octal numbers o4 is: "<<o4<<std::endl;
- return 0;
- }
Hexadecimal literals
Hexadecimal literals can be declared by prefixing them with 0x or 0X:
- #include<iostream>
- int main ()
- {
- int h1 = 0x42; //int h1 = 66;
- int h2 = 0xfa; //int h2 = 250;
- int h3 = 0XA; //int h3 = 10;
- int h4 = 0XB; //int h4 = 11;
- int h5 = 0x0; //int h5 = 0;
- int h6 = 0x1; //int h6 = 1;
- std::cout<<"Hexadecimal number h1 is: "<<h1<<std::endl;
- std::cout<<"Hexadecimal number h2 is: "<<h2<<std::endl;
- std::cout<<"Hexadecimal number h3 is: "<<h3<<std::endl;
- std::cout<<"Hexadecimal number h4 is: "<<h4<<std::endl;
- std::cout<<"Hexadecimal number h5 is: "<<h5<<std::endl;
- std::cout<<"Hexadecimal number h6 is: "<<h6<<std::endl;
- return 0;
- }
Binary literals
C++14 introduces binary literals which are prefixed by 0b or 0B:
- #include<iostream>
- int main ()
- {
- int b1 = 0b11111010; // int b1 = 250;
- int b2 = 0b010000111; // int b2 = 135;
- int b3 = 0b0100000111; // int b3 = 263;
- int b4 = 0B01001100110011; // int b4 = 4915;
- int b5 = 0B010011; // int h5 = 19;
- std::cout<<"Binary number b1 is: "<<b1<<std::endl;
- std::cout<<"Binary number b2 is: "<<b2<<std::endl;
- std::cout<<"Binary number b3 is: "<<b3<<std::endl;
- std::cout<<"Binary number b4 is: "<<b4<<std::endl;
- std::cout<<"Binary number b5 is: "<<b5<<std::endl;
- return 0;
- }
Separating literals with apostrophes
To improve readability of long literals, C++14 allows separation of digits with apostrophes:
- #include<iostream>
- int main ()
- {
- long d = 6'546'687'616'861'129l;
- int b = 0b101'1001'0011'1010'1101'1010'0001;
- unsigned long ulX = 0x139'ae3b'2ab0'94f3;
- const long double pi = 3.141'592'653'589'793'238'462l;
- std::cout<<"The numbers are; " "\n";
- std::cout<<"Long: "<<d<< "\n"
- <<"Integer: "<<b<<"\n"
- <<"Unsigned long: "<<ulX<<"\n"
- <<"Constant pi: "<<pi<<"\n"<<std::endl;
- return 0;
- }
String Literals
String literals can be typed as arrays of char. However, we can better and conveniently work with strings using string type from the library <string> . With this approach, long text can be split into multiple sub-strings.
- #include<iostream>
- #include<string>
- int main ()
- {
- char s[] = "Array of Characters"; // String as array of characters, Old C style
- std::string s1 = "Better way to work with strings";
- std::string s2 = "This is part one of a long text; "
- "here is the second part.";
- std::cout<<"Index the first string: "<<s[0]<<"\n"
- <<"The second string: "<<s1<<"\n"
- <<"The longest string: "<<s2<<std::endl;
- return 0;
- }
Non-narrowing Initialization
Sometimes very long values, e.g., const long double pi = 3.141'592'653'589'793'238'462l; may be printed with the leading bits cut off. To prevent this, C++11 introduces an initialization that makes sure no data is lost, i.e., values are not narrowed. This is achieved with the Uniform Initialization or Braced Initialization. If the variable can hold the value on the target architecture, values in the braces cannot be narrowed. In this case, values do not lose precision on initializations provided that they are representable with the declared types.
- #include<iostream>
- #include<string>
- int main ()
- {
- long l1 = 1234567890123;
- long l2 = {1234567890123};
- std::cout<<"This number "<<l1<<" may be narrowed,""\n"
- <<"but this one "<<l2<<" cannot."<<std::endl;
- return 0;
- }
Scopes
Scopes determine the lifetime and visibility of (non-static) variables and constants and contribute to establishing a structure in C++ programs.
Non-static variable is like a local variable and they can be accessed through only instance of a class. A static member variable of a class is shared between instances of the class. In other words, it doesn’t belong to any instance. A static variable defined in a function is only accessible in the function, but persists across function calls.
Global definition
Every variable used in a C++ program must have been declared with its type specifier at an earlier point in the code. A variable may either have the global or local scope. A global variable is declared outside all functions. After their declaration, global variables can be referred to from anywhere in the code, even inside functions. It is not advised to use global variables as it may become very difficult to track them, especially as your code grows.
Local definition
A local variable is declared within the body of a function. Its visibility/availability is limited to the enclosed block, { } of its declaration. The scope of a variable starts with its declaration and ends within the closing brace of the declaration block. In the example below, the definition of \(\pi\) is limited to the block { } within the function, and an output outside the block {} but within the function is outside the scope and will thus cause an error:
- #include<iostream>
- int main ()
- {
- {
- const double pi = 3.14159265358979323846264338327950288419716939;
- std::cout<<" pi is "<<pi<<".\n";
- }
- //This will cause an error
- std::cout<<"pi is "<<pi<<".\n"; // error: ‘pi’ was not declared in this scope
- }
Hiding
When a variable with the same name exists in nested scopes, then only one variable is visible. The variable in the inner scope hides the other variables with the same name in the outer scopes. Run the code below to see various scopes for the variable a:
- #include<iostream>
- int main ()
- {
- int a = 5; // Define a #1
- std::cout<<"The value of a#1 is: "<<a<<std::endl;
- {
- a = 3; // Re-assign a #1, a #2 is not declared yet
- int a; // Declare a #2
- a = 8; // Assign a #2 , a #1 is hidden
- std::cout<<"The value of a#2 is: "<<a<<std::endl;
- {
- a= 7; // Re-assign a #2
- std::cout<<"The value of a#2 is: "<<a<<std::endl;
- }
- } // End of a #2 scope
- a = 11; // Re-assign a #1 (a #2 out of scope)
- std::cout<<"The value of a#1 is: "<<a<<std::endl;
- return 0;
- }
Hiding causes variables to have a distinguished lifetime and the visibility. In an example above, a #1 lives from its declaration until the end of the main function. However, it is only visible from its declaration until the declaration of a #2 and again after closing the block containing a #2. In fact, the visibility is the lifetime minus the time when it is hidden. Unfortunately, the hiding makes the homonymous variables in the outer scope inaccessible.
Static variables live till the end of the execution but are only visible within the scope.
Subprograms (Functions)
A function is a subprogram that may be reused in different parts of the program. Functions reduce verbosity and help in structuring the code. Usually, functions have a name, zero or more arguments with a fixed type, and a return type. The special return type void indicates a function that doesn’t return anything.
Example
- #include <iostream>
- using namespace std;
- // Expects one double argument, returns double
- double square (double x)
- {
- return x * x;
- }
- // Expects an int and a double as arguments (args), returns nothing
- void printSquares(int a = 10, float b = 20)
- {
- std::cout << square(a) << " and " << square(b) << std::endl;
- }
- int main()
- {
- printSquares(1, 1);
- printSquares(30, 40.123);
- printSquares(100, 100.10);
- printSquares(30, 40.123);
- }
Call by Reference
C++ is one of the languages that always create copies of arguments when a function is called (call-by-value). This means that local changes to these variables don’t modify the original values. In some cases, the local names refer to the actual memory locations that were passed to the function (call-by-reference). In C++, one can pass a pointer or reference instead.
Call-by-reference is also often used when a function should return more than one value: one emulates this by modifying one or more reference arguments. For large entities (e.g., vectors, matrices) it often makes sense to pass them by reference even if they should not be modified, since this avoids costly copy operations (both in terms of runtime and memory use).
References
- #include <iostream>
- int main (){
- int a = 5;
- std::cout<<"Print a: "<<a<<"\n";
- int& b = a; // b is an alias for a
- b = 4; // this changes a as well
- std::cout<<"Print b: "<<b<<"\n";
- std::cout<<"Print a: "<<a<<"\n";
- }
Pointers
Each type T, whether built-in or user-defined, has an associated type T* (pointer to T ) defined, with the meaning the “address of a value of type T in
memory”.
An ampersand & in front of a variable produces its address, while an asterisk * in front of a pointer dereferences the pointer, providing access to the variable itself. The keyword new can be used to acquire a slot in memory that is unnamed, i.e., not associated with a variable.
Example
- #include <iostream>
- int main (){
- int i = 5;
- int* p = &i; // Pointer
- std::cout<<"Print i: "<<&p<<"\n"; // Obtain address
- std::cout<<"Print i: "<<*p<<"\n"; /* Dereference the pointer, providing access to the variable itself. */
- int* p2 = new int;
- std::cout<<"Print p2: "<<&p2<<"\n";
- std::cout<<"Print p2: "<<*p2<<"\n";
- *p2 = 4;
- std::cout<<"Print p2: "<<&p2<<"\n";
- std::cout<<"Print p2: "<<*p2<<"\n";
- delete p2;
- std::cout<<"Print p2: "<<&p2<<"\n";
- std::cout<<"Print p2: "<<*p2<<"\n";
- }
Function Templates
Often, one has to define the same functionality for several different data types. This can become tedious, both during initial implementation and when fixing bugs. C++ provides a language feature for this, where all the different versions are auto-generated from a special construct, called a function template.
Example
- #include<iostream>
- template<typename T>
- T square(T x)
- {
- return x * x;
- }
- /*
- A function square<T> is then available
- for any type T that has a multiplication operator *
- */
- int main()
- {
- int i = square<int>(5); // int version
- float f = square<float>(27.f); // float version
- double d = square<double>(3.14); // double version
- std::cout<<"Print i: "<<i<<"\n";
- std::cout<<"Print f: "<<f<<"\n";
- std::cout<<"Print d: "<<d<<"\n";
- }
Data Type Templates
Function definitions aren’t the only use case for templates. One can also automate the generation of data types. These are known as class templates since structs are a special case of classes in C++. Function templates and class templates were the only types of templates until C++14, when – variable templates were introduced.
Example of data type templates
- template<typename T>
- struct Pair {T a; T b;};
- int main() {
- Pair<int> ip; // A pair of ints
- Pair<float> fp; // A pair of floats
- // Pair<int> is a data type, and can be used as such
- Pair<Pair<int>> ipp; // Pair of pair of ints
- }
Classes/Methods
The original name of C++ was “C with classes”, so classes, objects, and object-oriented programming are clearly an important part of C++.
While a classic C struct is simply an aggregation of data, C++ structs and classes typically contain methods, functions that are closely linked to the data members and parts of their type definition. A class in C++ is the building block that leads to Object-Oriented programming -- a blueprint for creating OOP or objects. We can simply define a class as a user-defined data-type which serves as a blueprint for creating objects. The class contains at least one access specifier, and holds its own data members (data variables) and member functions (user-defined data types), which can be accessed and used by creating an instance of that class.
A class in C++ is defined using keyword class followed by the name of the class (Diagram taken from here):
![Image](https://media.geeksforgeeks.org/wp-content/cdn-uploads/Classes-and-Objects-in-C.png)
Objects Declaration
An object is an instance (each realized variation of that object) of a class. During class definition, no memory is allocated until the class is instantiated (i.e. an object is created).
When a class is defined, only the specification for the object is defined without memory or storage allocation. To use the data and access functions defined in the class, you need to create objects. The data members and member functions of a class can be accessed using the dot operator with the object. Although public members are accessed as previously explained, the private data members are not allowed to be accessed directly by the object. Accessing a data member depends on the access control of that data member. The access control is given by Access modifiers in C++. There are three access modifiers, namely, public, private, and protected.
Example of a class
- // C++ program demonstrating data members access
- #include <bits/stdc++.h>
- using namespace std;
- class Names
- {
- // Access specifier
- public:
- //Declare data Members
- string myName;
- // Member Functions()
- void printname()
- {
- cout << "My name is " << myName<<"\n";
- }
- }; // A class definition ends with semicolon
- int main() {
- // Declare objects of class Names
- Names obj1;
- Names obj2;
- // Access data member
- obj1.myName = "Mishaeli"; // Class instance/create object
- obj2.myName = "Hamisi";
- // Access member function
- obj1.printname();
- obj2.printname();
- return 0;
- }
Member functions can be defined inside or outside the class definition.
To define a member function outside the class definition requires scope resolution :: operator along with class name and function name:
- // Demonstrating function declaration outside class
- #include <bits/stdc++.h>
- using namespace std;
- class Names
- {
- public:
- string myName;
- string myID;
- // printname defined inside class definition
- void printname();
- // printid defined inside class definition
- void printid()
- {
- cout << "My ID is " << myID<<"\n";
- }
- };
- /* Definition of printname outside class
- definition using scope resolution operator:: */
- void Names::printname()
- {
- cout << "My name is " << myName<<"\n";
- }
- int main() {
- Names obj1;
- Names obj2;
- obj1.myName = "Jesca";
- obj2.myName = "Miguel";
- obj1.myID= "1512367TRE";
- obj2.myID= "XDT12905634";
- // Call printname()
- obj1.printname();
- obj2.printname();
- cout << endl;
- // Call printid()
- obj1.printid();
- obj2.printid();
- return 0;
- }
Constructors
Constructors are special class members which are called by the compiler every time an object of that class is instantiated. Constructors have the same name as the class and maybe defined inside or outside the class definition. There are 3 types of constructors, namely Default constructors, Parameterized constructors, and Copy constructors. A Copy Constructor creates a new object, which is an exact copy of the existing object. The compiler provides a default Copy Constructor to all the classes.
Example of constructors
- // C++ program to demonstrate constructors
- #include <bits/stdc++.h>
- using namespace std;
- class Names
- {
- public:
- string myID;
- //Default Constructor
- Names()
- {
- cout << "Calling default Constructor" << endl;
- myID="URTD123";
- }
- // Parameterized Constructor
- Names(string x)
- {
- cout << "Calling parameterized Constructor" << endl;
- myID=x;
- }
- //Copy constructors
- Names (Names &){}
- };
- int main() {
- // obj1 will call Default Constructor
- Names obj1;
- cout << "My ID is: " <<obj1.myID<< endl;
- // obj2 will call Parameterized Constructor
- Names obj2("CDTRE3452");
- cout << "My ID is: " <<obj2.myID << endl;
- return 0;
- }
Destructors
Destructor is a special member function that is called by the compiler when the scope of the object ends.
Classes and Dot (.) Operator
Consider a class complex:
- #include<iostream>
- class complex
- {
- public : //Public data members
- double r , i;
- };
- int main()
- {
- complex z , c; //Create objects
- z . r = 3.5; z.i = 2;
- c . r = 2; c . i = - 3.5;
- std::cout<<"z is ("<< z.r<<", " << z.i << ")\n";
- }
This code above defines the objects z and c with variable declarations, in the same way we declare intrinsic types -- a type name followed by a variable name or a list of names. The members of an object can be accessed with the dot operator/notation as illustrated above.
Accessibility
Each member of a class has a specified Accessibility. C ++ provides three of them:
- public: Accessible from everywhere.
- protected: Accessible in the class itself and its derived classes.
- private: Accessible only within the class.
Struct
C++ has the struct keyword from C which declares a class as well, with all features available for classes. The only difference is that all members are by default public.
Therefore, in C++
- struct xyz
- {
- ...
- };
is the same as:
- class xyz
- {
- public:
- ...
- };
Access Operators
We have seen that we can select the member of a class by using the dot (.) operator. The rest of the access operators are pointer-related in some way.
Let us consider a pointer to the class complex and how to access member variables through this pointer:
- #include<iostream>
- class complex
- {
- public : // Public data members
- double r , i;
- };
- int main()
- {
- complex c ;
- //Pointers to the class complex
- complex* p = &c;
- //* p . r = 3.5; // This will cause error, it requires *( p.r)
- (*p).r = 3.5;
- p->r = 3.5;
- }
But, accessing members through pointers is not favoured since the dot operator has a higher priority than the dereference *.
Abstraction using Classes
Class helps us to group data members and member functions/methods using available access specifiers. A Class can decide which data member will be visible to outside world and which is not.
In the program below, we are not allowed to access the variables a and b directly, however, we can call the function indirect_access() to set the values a and b and and print them:
- #include <iostream>
- using namespace std;
- class abstract
- {
- private:
- int a, b;
- public:
- //set values of the private members
- void Indirect_Access(int x, int y) //Public method
- {
- a = x;
- b = y;
- cout<<"a = " <<a <<" and "
- <<"b = " << b << endl;
- }
- };
- int main()
- {
- abstract obj;
- obj.Indirect_Access(678, 758);
- return 0;
- }
Recommended IDEs
KDevelop (best for Linux), Eclipse, Visual Studio, and Code::Blocks (a free, open-source cross-platform IDE that supports multiple compilers including GCC, Clang and Visual C++).
References
1. Discovering Modern C++: An Intensive Course for Scientists, Engineers, and Programmers (C++ In-Depth Series), Addison-Wesley Professional; First Edition (December 17, 2015)