2.6. Converting Formulas to C++ Statements

Time: 00:06:35 | Download: Large, Large (CC), Small | Streaming, Streaming (CC) | Slides (PDF)

Computer scientists created FORTRAN (FORmula TRANslation), the first high-level computer programming language, to simplify translating mathematical formulas into computer code. From FORTRAN forward, most general-purpose programming languages make the task relatively easy. C++ directly implements basic operations like addition, subtraction, multiplication, and division with the compiler. It implements more complex operations, like evaluating 35 or √2, with library functions. Libraries also provide constants like π. The linker or loader automatically scans the C++ libraries, linking needed features with the executable program.

This section offers general guidelines for converting mathematical formulas into C++ code. Many will recognize some example formulas, but few will recognize all. Don't be too concerned with the formulas themselves, just focus on converting them to C++.

Interference

Interference occurs when a previously learned concept interferes with or obstructs learning a new, often similar, concept. Previous experience with Python or Java can interfere with learning some C++ operations. Consciously focusing on the similarities and differences helps separate and clarify the concepts.

Python
Python has an exponentiation operator, **, C++ does not.
Java
Java is a pure object-oriented language that defines all constants and methods in a class. C++ is a hybrid language with many constants and functions defined outside any class:
Java C++
Math.PI M_PI (a non-standard constant)
Math.sqrt(double x) sqrt(double x)
Math.pow(double base, double exponent) pow(double base, double exponent)

Variables

Choosing "good" variable names is one of the first steps programmers take to write understandable, self-documenting code. Good variable names have meaning in the context of the problem the program solves. But what constitutes "meaning" is unavoidably ambiguous.

Formulas \[ax^2 + bx + c = 0 \] \[x = {-b \pm \sqrt{ b^2 - 4ac} \over 2a } \] \[ e = mc^2 \]
Variables and Constants a, b, c, and x e and m
energy and mass
C
 (a)(b)
Formulas and "good" variable names. Mathematical formulas often use a single character to represent a variable. The problem's "context" is the formula, suggesting that the formula's variables are "good" program variables. Sometimes, the formula variables represent concrete features that programmers can leverage as program variable names.
  1. When describing a specific problem, the variables in the quadratic equation and formula may represent concrete problem features, but in the general case, they represent dimensionless numbers. The single-character formula names are meaningful in the formula's context and are appropriate program variable names.
  2. Einstein's mass-energy equivalence equation relates mass (m), energy (e), and the speed of light (c). Programmers can use the formula names or the physical properties they represent. The constant c is well-known to anyone using the equation, and programmers can appropriately create a symbolic constant, C, to represent it.

Sometimes, variables are accompanied by superscripts and subscripts. Superscripts (e.g., xn) usually denote an exponentiation operation (described below) and are how the textbook uses them. However, subscripts often distinguish between distinct but related variables.

Formulas \[ l = l_0 \sqrt{1 - { v^2 \over c^c}} \] \[ F = {Gm_1m_2 \over r^2} \] \[ F = {Gm_sm_e \over r^2} \] \[ x_{n+1} = x_n - { f(x_n) \over {f'(x_n)}} \] \[ F_n = F_{n-1} + F_{n-2} \]
Variables
and
Constants
l0 and l
l_initial and l_final
lInitial and lFinal
m1 and m2
me and ms
m_earth and m_sun
mEarth and mSun
G
x or x0 and x1
x_new and x_old
xNew and xOld
F, F1, and F2
 (a)(b)(c)(d)
Variable names with subscripts. Programmers can't put subscripts in C++ variable names. So, while being careful to make sure each variable name is unique and legal, make the subscript part of the variable name or expand the name to reflect the corresponding problem properties.
  1. The Lorentz contraction formula describes an object's length while in motion (typically near the speed of light, c) from its length at rest, l0 and velocity, v. As in the previous figure, c is the speed of light.
  2. Newton's law of gravitational force between two masses, m1 and m2. G is the universal gravitational constant. In the specific case of the Earth and Sun, the formula might read me and ms, and programmers might choose program variables naming them.
  3. The Newton-Raphson approximation formula iteratively (i.e., in a loop) refines an initial, approximate function solution, xn, calculating a more accurate solution, xn=1. The subscript n designates the variables belonging to a specific iteration or loop, so the formula calculates the new, refined solution from the old, previous one.
  4. Like (c), the Fibonacci sequence also iterates or loops, calculating a new value based on previous ones. Finding "good" names is more challenging than in the previous example, but we could use F, F1, and F2. What names would you choose?
Variable names cannot contain spaces. Java programmers use camel notation (aka camel case) when creating variable names. Single-word names consist only of lower-case letters; multiple-word names begin with a lower-case letter but capitalize the first letter of subsequent words. C++ doesn't have an established naming convention, and programmers frequently use the underscore character in multi-word names but may also use camel notation.

Multiplication And Division

Multiplication and division are familiar arithmetic operations. However, that familiarity can cause us to make trivial but fatal errors when converting the operations to C++.

Formula C++
PR P * R
principle * rate
x(y + z)
(w + x)(y + z)
x * (y + z)
(w + x) * (y + z)
Multiplication. Mathematical formulas typically denote multiplication by juxtaposing variables or expressions (i.e., placing the variables or expressions adjacent to one another). However, C++ requires programmers to indicate explicitly the operation with the "*" operator. The requirement is easy to understand if we expand the single-character names to full words (requiring us to know what the formula variables mean) because, for example, the compiler treats PR as a single variable. Unfortunately, parentheses in the formula obscure this visual reminder, increasing the chance of an error.
Formula C++
P / T
\[ P \over T \]
P / T
pressure / temperature
\[ P \over {T_1 - T_1} \] P / (T1 - T2)
Division. In the case of simple division, the C++ code is identical, or nearly identical, to the corresponding mathematical formula. The horizontal line represents a division operation. When the numerator or denominator is a more complex expression, especially one involving addition or subtraction, programmers must treat it as a sub-expression by surrounding it with grouping parentheses (red). The extent of the horizontal line (how far it reaches left to right) establishes the elements in the group. In the last example, the grouping parentheses force subtraction to take place before division. Correctly converting these formulas requires a basic knowledge of high school algebra.
Formula C++
\[c = {5 \over 9} (f - 32) \] c = 5.0 / 9.0 * (f - 32);
c = 5 * (f - 32) / 9;
c = 5.0 / 9 * (f - 32);
c = 5 / 9.0 * (f - 32);
c = (double)5 / 9 * (f - 32);
c = double(5) / 9 * (f - 32);
c = 5 / (double)9 * (f - 32);
c = 5 / double(9) * (f - 32);
Integer Division. The ftoc.cpp example described integer division's behavior and explained why the illustrated C++ code works. Programmers' primary responsibility is recognizing and understanding integer division. Specifically, knowing when it's appropriate and how to avoid it when it isn't.

Unary Minus / Negation Operator

Formula C++
-N -N
-(n - x) -(n - x)
-n + x
x - n
Negation. Everyone is familiar with arithmetic subtraction, implemented with the binary minus operator: x - 5. However, there is also a unary version known as the negation operator. Sometimes, when a new programmer sees an expression in a mathematical formula like -N, they implement it in C++ code as -1 * N. Although this works, it is also unnecessarily complex and awkward.

The <cmath> Header File

#define _USE_MATH_DEFINES	// Microsoft only, for M_PI
#include <iostream>
#include <cmath>
using namespace std;

int main()
{
	double	radius = 5.0;
	double	area = M_PI * pow(radius, 2);
	return 0;
}
The <cmath> Header File. Chapter 6 explores functions in great detail, so at this point, it's sufficient for us to know that programs must declare functions, including library functions, before using them. Similarly, they must also specify symbolic constants before using them. Programmers provide the mathematical declarations and specifications by including the <cmath> header file.

Exponentiation And The pow Function

baseexponent
x2 = x * x;
x3 = x * x * x;
xn = x1 * x2 * . . . * xn;
double exp = 1;
for (int i = 0; i < 57; i++)
	exp = exp * x;
(a)(b)(c)
Small, integral exponents. Programmers often calculate expressions with small, integral exponents by multiplying the expression (variable) by itself because (you know by now) C++ doesn't have an exponentiation operator.
  1. Generally, base and exponent are floating-point expressions. However, in many formulas, exponent is a small, positive integer.
  2. Explicit self-multiplication works well if the base expression is a variable or otherwise small and if the exponent is a small positive integer: n ≤ 3. If the base is relatively simple (a single variable or a simple sum or product), squaring or cubing it is likely more efficient than using the pow function.
  3. Self-multiplication becomes tedious and error prone for n > 3 (approximately). For larger values of n, a for-loop can be used but rarely is.
Please see The pow library function.
Formula C++
x57 pow(x, 57)
x2/3 pow(x, 2.0 / 3.0)
x-N pow(x, -N)
xy pow(x, y)
(1 + R)-N pow(1 + R, -N)
(1 + i)-(n - x) pow(1 + i, -n + x)
(x + 1)(1 + i)-(n - x) (x + 1) * pow(1 + i, -n + x)
The pow function. Formulas with exponents > 3 are not common in general computing, but negative and floating-point exponents are, and for these problems, we use the pow function. The program evaluates the sub-expressions 1 + R, 1 + i, and -n + x before calling the pow function, and passes the resulting values as functional arguments. It's unnecessary to surround the sub-expressions with grouping parentheses because the comma unambiguously separates them. In the last example, the pow function call forms a sub-expression, so grouping parentheses around the call is unnecessary.

Square Root And The sqrt Function

Formula C++
\[ \sqrt{a} \] sqrt(a)
pow(a, 0.5)
\[ \sqrt{a^2+b^2} \] sqrt(a * a + b * b)
sqrt(pow(a, 2) + pow(b, 2))
\[ \sqrt{a^2}+b^2 \] sqrt(a * a) + b * b
sqrt(pow(a, 2)) + pow(b, 2)
\[ h \sqrt{a^2+b^2} \] h * sqrt(a * a + b * b)
h * sqrt(pow(a, 2) + pow(b, 2))
The sqrt function. Mathematical formulas denote the square root operation with a radical (√) symbol, which programmers convert to the C++ sqrt function. The extent of the horizontal line at the top of the radical indicates the elements included in the square root operation (i.e., it operates as a grouping symbol). All the parentheses in the examples indicate function calls - delimiting the function's argument - and not grouping parentheses. In the last example, the sqrt function forms a sub-express that does not require grouping parentheses around it.

It is illegal, both mathematically and with the sqrt function, to attempt to take the square root of a negative value. If a program passes a negative value as the sqrt function's argument, it will return a strange value, such as -1.#IND or -1.#INF.

Please see The sqrt library function.

Symbols of Inclusion and Grouping Parentheses

Formula C++
\[P = F\left[ r \over {(1 + r)^n - 1} \right] \left[ 1 \over {1 + r} \right] \] P = F * (r / (pow(1 + r, n) - 1)) * (1 / (1 + r));
P = F * r / (pow(1 + r, n) - 1) / (1 + r);
Symbols of inclusion. Mathematical formulas typically use round parentheses for grouping, just like computer programming languages. But unlike programming languages, mathematical formulas can and sometimes do use other symbols to denote groups. Formulas typically use additional symbols when multiple grouping levels are required - using different symbols makes matching the corresponding opening and closing symbols easier. In addition to round parentheses, mathematical formulas also use square brackets and braces. C++ uses brackets and braces for different purposes, so programmers must translate them into round parentheses. The examples show the grouping parentheses in red.

As formulas' size and complexity increase, so do the number of ways programmers can translate them to C++. As originally stated, a formula has meaning to someone who uses it, so, to the extent possible, making the C++ code look like the formula is a good rule of thumb (first version). However, sometimes, we can rearrange the code, making it more efficient or compact (second version).

It is possible to add more parentheses. There is no correct number, but extraneous parentheses become problematic, making it more difficult to read and understand a statement and more likely that the parentheses will become unbalanced or misplaced. Relying on operator precedence will help to keep the number of parentheses at a manageable level.

Temporary Variables And Intermediate Calculations

Occasionally, breaking a formula into smaller parts by saving intermediate calculations in temporary variables is helpful and appropriate. It's easy to abuse this practice, and in extreme cases, it leads to ludicrous code.

Formula C++
\[x = {-b \pm \sqrt{ b^2 - 4ac} \over 2a } \]
double d = b * b;
double e = 4 * a * c;
double f = d - e;
double g = sqrt(f);
double h = 2 * a;
double i = -b + g;
double j = -b - g;
double x1 = i / h;
double x2 = j / h;
Misusing temporary variables. Inexperienced programmers sometimes use temporary variables as a substitute for learning operator precedence and associativity. The C++ code illustrated here is excerpted from an assigned program to calculate the roots of a quadratic equation. The program defines the variables a, b, and c, using them to calculate the roots x1 and x2. These variables have meaning in the context of the quadratic formula, but the other variables do not. The fragmented calculations and meaningless variables make the code challenging to read and understand. (Happily, the student learned to use precedence and associativity and did well in the course!)
Formula C++
\[ discriminant = b^2 - 4ac \]
double discriminant = b * b - 4 * a * c;

if (discriminant < 0)
{
	double real = -b / (2 * a);
	double imag = sqrt(-discriminant) / (2 * a);
}
else
{
	double x1 = (-b + sqrt(discriminant)) / (2 * a);
	double x2 = (-b - sqrt(discriminant)) / (2 * a);
}
A valid temporary variable. Quadratic equations can have different kinds of roots; which kind a specific equation has depends on a value called the discriminant. The discriminant is the part of the quadratic formula under the radical (i.e., the square root function's argument). The discriminant is sufficiently meaningful in the context of quadratic equations that mathematicians have named it. Furthermore, it has distinct applications in a general quadratic formula program, as the code illustrates.