Numbers are what make the computer’s world go ‘round, so let’s examine ways to get the computer to handle those numbers for us.
Variables
Variables might seem like the letters we use in algebra – y = mx + b, that sort of thing – but in C++ they’re just places to store values. Example 3-1 shows what it looks like when we create variables.
Variable declarations for my American Idol obsession
This gives us an integer variable; a float variable, which can take decimal places; and two double variables, which can take more decimal places. (How many more depends on the machine you’re on.)
The trailing F on 432.5F means it’s a float, not a double, value. (If you don’t specify, it’s a double.) If you get these confused, you may get a warning from the compiler. To avoid the warning, I use double and forget the F.
1.0E-21 is how C++ writes 1.0 × 1021.

Variables storing values in main
We gave these variables values soon as we made them, on the same line. We didn’t have to, but it’s good practice. It’s disappointing when you find that the number of dollars in your bank account is –68 million, because you didn’t tell the computer what value to initialize it with, and it just happened to start with a very inappropriate number.
Initialize them.
It’s also good to make descriptive names like the preceding ones. It’s frustrating to search through code trying to find out what "z" or "x" means. But you know exactly what seasonsOfAmericanIdol means.
Variable names start with letters (possibly preceded with _’s), but after that they can have numerals in them. Capitalization matters: temp and Temp are different variables.
Variable and constant names should be descriptive and shouldn’t be the same as any of the built-in keywords in C++ (const, int, void, etc.).
By convention, C++ constants are written in ALL CAPS to SCREAM at the programmer that this is a CONSTANT, not a variable, value. To separate words jammed together, use _: MAX_LENGTH, for example.
Conventions for variable names are flexible. I use “camel case” variables: you jam words together to make a variable, capitalize all first letters of words except for the first – firstEntry, minXValue. I reserve initial capitals for created types like SSDL_Image. Initial _’’s are for the compiler’s own identifiers. There are other conventions; whatever convention you use, it’s best to be clear as possible.

How do you think it got its name?
Constants
What’s the difference? const simply means “does not change.” constexpr means “does not change and gets set at compile time.” The latter makes programs a little faster. We won’t notice with the preceding declarations, but as programs get bigger and more complex, it may matter more. (I can’t do it with SSDL_CreateColor or SSDL_OpenSystemFont, because those won’t work till SDL is started at runtime.)
I use constexpr when the initial value is just numbers (like 3.14159265359 or 7+7) and const when it’s a function call (like SSDL_OpenSystemFont ("times", 18)). Eventually we’ll refine that (see Chapter 26), but it’s good for now.
When to use constants, not literal values
When should I use a literal value, like 100, and when should I use a constant symbol, like CENTURY? The answer is almost always: use the constant rather than the bare literal value. There are two reasons:
One is to be clear, as shown earlier. You’re going back through a program, and you see a reference to 7. Seven what? Days in the week? The number of deadly sins? The age you were when you wrote your very first program? You’ll have to do detective work to figure it out, especially if there’s more than one 7 in your program. Detective work is not for the lazy. Better to document it with a clear name.
The other reason is to easily change the value. For example, there are by convention seven deadly sins, but using bare numeric literals like 7 is a pretty deadly sin in programmer terms. So maybe that constexpr int NUMBER_OF_DEADLY_SINS = 7; needs to be updated to 8. If you used this constant, you’ve got one line to change. If you put 7 all through your program, you’ll have to go through figuring which 7’s to change and which ones to leave alone. Detective work again.
The bottom line is clarity. We won’t go back to the bug face program in Chapter 1 and replace all those numbers with constexprs, because it would make the program harder to follow; each value is unique, and naming it doesn’t make it clearer. (We have comments to show what it means anyway.) But the bug face program is the exception. Generally, values should be named.
Any time it’s not blindingly obvious what a numeric literal value is for, define it as a constant symbol, in ALL CAPS, and use that name whenever you refer to it.
constexpr to the Bug’s-Head Program
Yes, I concede – grudgingly – that the bare numeric literals in Chapter 1’s bug’s-head program can stay, for reasons given earlier. But what if we refer to values more than once? Use them to calculate positions of parts of the face? In such a case, we need constants. So

A bug’s head, drawn using constants and calculations
Math operators
The arithmetic operators
Operator | Meaning |
---|---|
+ | Addition |
- | Subtraction, negation |
* | Multiplication |
/ | Division |
% | Modulus |
Integer division
Back before you learned fractions, when you only used whole numbers, the result was always a whole number: 5 divided by 2 was 2, with a remainder of 1. It’s the same for C++’s integer division: 5/2 gives you another integer, 2, not 2.5 – that’s a floating-point value.
This can be confusing. 1/2 sure looks like it should be 0.5, but since 1 and 2 are integers, 1/2 has to be an integer too: 0.
In keeping with the way we divide integers, C++ also provides % , the modulus operator, which means “divide and take the remainder.” 5%2 gives us 1, the remainder after dividing 5 by 2. We’ll see more of % in Chapter 8, in section “Random numbers.”
Assignment (=) operators
The latter means take whatever number is in that seasonsOfAmericanIdol memory location, add 1 to it, and put the resulting value back into that same place.
It can also be written this way: seasonsOfAmericanIdol + = 1;.
They mean the same thing: add 1 to seasonsOfAmericanIdol.1
It works for other arithmetic operators: -=, *=, /=, and %= are all defined the same way.
A diving board example
A program to show a diver’s path, using constexprs and math operators
I initialize all variables, as always.
There are no bare numeric literals in any calculation or variable initialization; it’s CONSTANT values all the way.
I repeat the same pair of lines six times. Seriously? Is that lazy? We’ll have a better way in Chapter 5.

A program that shows the path of a diver into the water
That worked, and in some small sense evokes the terror I feel when I go off the high dive.
The no-worries list for math operators
Precedence : Consider a math expression, 2*5+3. In C++, as in an algebra class, we’d do the multiplying before the adding; this means (2*5)+3 = 13, not 2*(5+3) = 16. Similarly, in 8/2-1, we divide before subtracting. In general, do it the way that makes sense to you, and it’ll be right. If not, use parentheses to force it to go your way: 8/(2-1).
Associativity: In 27/3/3, which division comes first? Is it done like 27/(3/3), or (27/3)/3? Arithmetic operations are performed left to right. Assignment is done right to left: x=5+2 requires you to evaluate the 5+2 before doing anything to the x.
Coercion : If you want to cram a variable of one type into another, C++ will do it:
double Nothing = 0; // Nothing becomes 0.0, not 0
int Something = 2.7; // ints can't have decimal places , so
// C++ throws away the .7;
// Something becomes 2. No rounding, alas
If you mix integers and floating-point numbers in a calculation, the result will be the version with the most information, that is, floating point. 10/2.0, for example, gives you 5.0.
- 1.
Using constants for centimeters per inch (2.54) and inches per foot (12), convert someone’s height from feet and inches to centimeters, and report the result.
- 2.
Now do the reverse: centimeters to feet and inches.
- 3.Accumulate this sum for as far as you’re willing to take it, 1/2 + 1/4 + 1/8 + 1/16 +…, using +=. Do you think if you did it forever you would reach a particular number? Or would it just keep getting bigger? The ancient philosopher Zeno of Elea would have an opinion on that (https://en.wikipedia.org/wiki/Zeno%27s_paradoxes, at time of writing). But he’d be wrong.
You can’t get there from here. —Zeno. Sort of.
- 4.
Make a program to have a box move across the screen in 0.1-second jumps – like the diver moving, but clearing the screen at every jump so it looks like it’s really moving. Maybe make the delay shorter for a better illusion of motion.
Built-in functions and casting
Now I want to make a geometric figure, a five-point star. But forget Chapter 1’s graph paper. I want the computer to figure it out for me. Let it do its own (virtual) graph paper.
If I think of the star as inscribed in a circle…I probably know the center, so what I need calculated is the points at the edges. Each point is one-fifth of the way further around the circle than the previous, so if a circle is 360 degrees, the angle between them is 360/5 degrees. If you use radians rather than degrees, like C++, that’s 2π/5 radians between the points.


Sine and cosine as related to x and y
A star using sin and cos functions

A five-point star – at least, it was supposed to be
This will be a breeze to debug once we’ve covered the debugger in Chapter 9, but for now, we’ll just have to channel Sherlock Holmes. That line never changes, which means angle never changes, which must mean ANGLE_INCREMENT is 0. Why would it be 0?
Look at that calculation: ANGLE_INCREMENT = (2/NUMBER_OF_POINTS)*PI. The first thing to do is divide 2 by NUMBER_OF_POINTS, or 5. Since both are integers, we do integer division: 5 goes into 2 zero time (with a remainder of 2, for what it’s worth), so 2/5 gives us zero. Zero times PI is zero. So ANGLE_INCREMENT is zero.
We needed floating-point division.
One way is to force 2 and 5 to be float or double. You can do this by saying.
double (whatEverYouWantToBeDouble).
This is called casting.
double (2/NUMBER_OF_POINTS) won’t work because that divides 2 by 5, gets 0, and converts the 0 to 0.0. It’s still doing integer division.
A new beginning to main, to make Example 3-3 work

A five-point star
It’s not vertical, it seems. Exercise 1 is about turning it straight.
Other commonly useful mathematical functions include asin and acos (reverse sine and cosine), pow (raising a number to a power), abs (absolute value), and sqrt (square root). See Appendix F for more.
Antibugging
You call a value-returning function, but it has no effect. Here’s an example with a function we saw earlier:
// Center "Blastoff!" on the screen
SSDL_GetScreenWidth();
SSDL_RenderTextCentered (320, 240, "Blastoff!");
Sure, you called SSDL_GetWindowWidth()…but you never did anything with the result! C++ is happy to let you waste time by calling functions and not using what they give you. (It’s sort of a “let the programmer shoot self in the foot, and laugh” language.) If you want to use the value, refer to it wherever you want that value:
SSDL_RenderTextCentered(SSDL_GetScreenWidth ()/2,
SSDL_GetScreenHeight()/2,
"Blastoff!");
Or put it in a variable or constant for later use:
const int SCREEN_WIDTH = SSDL_GetScreenWidth ();
const int SCREEN_HEIGHT = SSDL_GetScreenHeight();
SSDL_RenderTextCentered (SCREEN_WIDTH/2, SCREEN_HEIGHT/2, "Blastoff!");
You divided two integers to get a floating-point number between zero and one, but you got zero. See Example 3-3 in this section. One of those operands of the / symbol should be cast to float or double.
You get a warning about conversion between types. You can ignore it, but to make it go away, cast the offending item to what you wanted. Then the compiler will know it was intentional.
- 1.
Adjust the star in Example 3-4 so that the star’s top point is straight up.
- 2.
Make a clock face: a circle with numbers 1–12 in appropriate places.
- 3.
(Harder) Here’s how to get system time in seconds:
#include <ctime >
...
int timeInSeconds = int4 (time (nullptr));
Use % and / operators to find the current time in hours, minutes, and seconds. The hours may be off due to what time zone you’re in; you can adjust appropriately.
- 4.
Having done 2 and 3, make a clock face that shows the current time.