C++ Datatypes

reading

Cyganek section 3.1

Data is stored in computer memory. We’ll refer to the data in memory as an object.

We can access this data:

  • directly: using a descriptive object name

  • indirectly: using a pointer or a reference to the memory location

Data stored in the object can be:

  • mutable : we can change it – these are variables

  • immutable : it cannot be changed – these are constants

There are several basic types of data:

  • char : a 1 byte character (like 'a')

  • int : an integer

  • float : a single precision floating point number

  • double : a double precision floating point number

Note

A floating point number is any number with a decimal point, like 2.0 or 1.e-20.

Generally, floating point numbers cannot be exactly represented on a computer, since there are infinite numbers on the number line between \([0, 1]\), but the computer only has a finite amount of memory.

Single and double precision refer to how much memory (and therefore how much precision) is used to store the numbers. Usually single precision uses 32 bits and double precision uses 64 bits.

More on this later.

There are also modifiers that can be used with many of these like: short, unsigned, long.

A table of C++ datatypes is provided in the CPlusPlus tutorial variables section

Initialization

We can initialize a define a variable as:

int i;
float x;
double slope;

Tip

C++ variable names are case-sensitive, so x and X are distinct.

Variable names can include letters, numbers, and _, but cannot begin with a number.

There are some reserved keywords (like int) that cannot be used as variable names.

It is always a good idea to initialize a variable with a starting value. Otherwise, most compilers will leave it undefined and you can run into problems if you try to use it without first remembering to initialize it.

There are a few ways to initialize. Here are the two we’ll see the most:

int i = 0;
double x{};

Both of these will initialize the variable to 0. The {} notation is more flexible, since it will work with more complicated objects that we will encounter later.

We’ll see a third way to initialize objects when we look at classes (and constructors).

Objects defined with const cannot be changed, so you are required to initialize the immediately:

const double G = 6.67e-8;

Tip

We can use ' to separate thousands in a number – this is called a digit separator. For example:

int a{1'234'567'890};

Mixing types

You need to be careful when mixing different data types. C++ will implicitly cast objects to the more general type, sometimes with unexpected consequences. Later we’ll see how to explicitly cast data if needed.

Try the following (you’ll need to put this in a main() function and compile it.

// convert from Fahrenheit to Celsius

double T_F {100.0};
double T_C =  (T_F - 32) * (5 / 9);

std::cout << "T_C = " << T_C << std::endl;

Why doesn’t this work as expected?

Sizes

The link above as well as your text tells you how the standard defines the sizes of the different data types.

Important

C++ guarantees a minimum size of the different types, but different compilers or processors may have different defaults.

But we can explicitly determine this with a C++ program using sizeof() – that returns the number of bytes

std::cout << sizeof(double) << std::endl;

The following is guaranteed:

sizeof(char) == 1  <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)

We can find out a lot about the range and precision of numbers that can be stored with a given type by using std::numeric_limits:

Listing 2 limits_test.cpp
#include <iostream>
#include <limits>

int main() {

    std::cout << "double: " << std::endl;
    std::cout << "  smallest value = " << std::numeric_limits<double>::min() << std::endl;
    std::cout << "  lowest value = " << std::numeric_limits<double>::lowest() << std::endl;
    std::cout << "  largest value = " << std::numeric_limits<double>::max() << std::endl;

    std::cout << "short: " << std::endl;
    std::cout << "  smallest value = " << std::numeric_limits<unsigned short>::min() << std::endl;
    std::cout << "  lowest value = " << std::numeric_limits<unsigned short>::lowest() << std::endl;
    std::cout << "  largest value = " << std::numeric_limits<unsigned short>::max() << std::endl;

    int i = 32767;
    std::cout << "i = " << i << std::endl;
    i++;
    std::cout << "i = " << i << std::endl;


}

Notice that there is a new pattern here, the use of <> – this is used in template functions, a topic we will talk a lot about later.

What happens if we exceed the limits of a data type?

https://imgs.xkcd.com/comics/cant_sleep.png

Fig. 7 (xkcd)

Let’s try this.

Precision is also important with floating point. Consider the following: what do you expect?

double a = 1.0;
double b = -1.0;
double c = 2.e-15;

std::cout << (a + b) + c << std::endl;
std::cout << a + (b + c) << std::endl;

With floating point, the associate property of addition does not hold.

try it…

Create a program that initializes a short integer and through addition triggers an overflow.