How Do We Compute With Integers and Doubles?

We can store numbers and track state. Now we want to transform state. What tools do we have to compute new values?

In Chapter 1, booleans had three operators: NOT, AND, and OR. Integers and doubles have more operators. This section covers the operations built into numeric types.

Computation Transforms State

So far, we have bound values directly:

int x = 5;

The value 5 came from us, typed literally into the code. But what if we want to compute a new value from existing bindings?

int a = 5;
int b = 3;
int sum = a + b;

Line 3 evaluates a (gets 5), evaluates b (gets 3), computes their sum (gets 8), and binds 8 to sum.

Let us trace the state:

after lineabsum
15
253
3538

The + operator transformed two values into a new one. This is computation. The pattern is: evaluate, transform, bind.


Addition, Subtraction, and Multiplication

These three operators work as you expect from arithmetic:

int sum = 5 + 3;         // 8
int difference = 10 - 4; // 6
int product = 6 * 7;     // 42

The // marks a comment. Comments are notes for humans that C# ignores. Here they show each expression’s result.

Each operator takes two integers and produces an integer.

Type signatures:

  • add: (int, int) → int
  • subtract: (int, int) → int
  • multiply: (int, int) → int

The translations:

  • a + b reads as “a plus b”
  • a - b reads as “a minus b”
  • a * b reads as “a times b”

These operators also work with doubles, producing doubles:

  • add: (double, double) → double
  • subtract: (double, double) → double
  • multiply: (double, double) → double

Exceeding the range. Integers have a limited range: -2³¹ to 2³¹ - 1. What happens if a computation produces a result outside this range?

The answer depends on when C# detects the problem.

If the compiler can see the overflow while checking your code, it reports an error:

int x = int.MaxValue + 1;  // Error: the compiler sees this overflows

But if the overflow happens while the program runs, C# cannot catch it in advance. Instead, the value wraps around:

int x = int.MaxValue;
int y = x + 1;             // No error: wraps to int.MinValue
Console.WriteLine(y);      // -2147483648

Why the difference? In the first case, int.MaxValue + 1 involves only literal values. The compiler evaluates it immediately, detects the overflow, and stops. In the second case, the addition involves a variable. The compiler cannot know what value x holds until the program runs, so it cannot catch the problem.

Overflow: Overflow occurs when a computation produces a value above 2³¹ - 1. The result wraps around to the negative range.

Underflow: Underflow occurs when a computation produces a value below -2³¹. The result wraps around to the positive range.

The wrapping follows a pattern. If you overflow by n, you land at int.MinValue + (n - 1). If you underflow by n, you land at int.MaxValue - (n - 1).

int x = int.MaxValue;
Console.WriteLine(x + 1);  // -2147483648 (int.MinValue)
Console.WriteLine(x + 2);  // -2147483647 (int.MinValue + 1)
Console.WriteLine(x + 3);  // -2147483646 (int.MinValue + 2)

Multiplication can also overflow. What is int.MaxValue * 2? The mathematical answer is roughly 4.3 billion, which overflows by about 2.1 billion. The result wraps to -2.

What about int.MinValue * 2? The mathematical answer is roughly -4.3 billion, which underflows by about 2.1 billion. The result wraps to 0.

This behavior can cause subtle bugs. If your program silently wraps from a large positive number to a large negative number, calculations go wrong without any error message. Be mindful of integer bounds when working with large values.


Try it yourself.

What value would each expression produce at runtime?

  1. int.MaxValue + 2
  2. int.MinValue - 1
  3. int.MinValue - 3
  4. int.MaxValue * 2
  5. int.MinValue * 2
Reveal answer
  1. int.MaxValue + 2 → -2147483647 (wraps to int.MinValue + 1)
  2. int.MinValue - 1 → 2147483647 (wraps to int.MaxValue)
  3. int.MinValue - 3 → 2147483645 (wraps to int.MaxValue - 2)
  4. int.MaxValue * 2 → -2 (overflows by ~2.1 billion, wraps around)
  5. int.MinValue * 2 → 0 (underflows by ~2.1 billion, wraps around)

If any answers differed, review the wrapping rules.


Try it yourself.

Translate this code to English:

int total = price + tax;

Write your answer before revealing ours.

Reveal answer

“Create an integer variable named total and bind the result of evaluating price plus tax to it.”

If your answer differed, note what you missed before continuing.


Try it yourself.

Write C# code for this description:

“Create an integer variable named area and bind the result of evaluating width times height to it.”

Reveal answer
int area = width * height;

If your answer differed, note what you missed before continuing.


Integer Division

Addition, subtraction, and multiplication work the way you expect. Division does not.

Before reading further, predict: what does 7 / 2 evaluate to?

int result = 7 / 2;
Console.WriteLine(result);
Reveal output

Output: 3

Integer Division: Integer division returns the quotient of two integers, discarding any remainder.

The type signature is divide: (int, int) → int. Two integers go in, one integer comes out. Since 3.5 is not an integer, C# removes the decimal part through truncation.

Truncation: Truncation is the removal of the decimal part of a number, keeping only the whole number portion.

ExpressionMathematical ResultC# Result
7 / 23.53
10 / 33.333…3
1 / 20.50
9 / 100.90
-7 / 2-3.5-3

Notice 1 / 2 evaluates to 0. The decimal part is removed entirely.

The translation: a / b reads as “a divided by b.” When both operands are integers, the result is truncated.

Division by zero throws a DivideByZeroException and stops your program.


Try it yourself.

Evaluate each expression by hand:

  1. 15 / 4
  2. 8 / 3
  3. 3 / 5
  4. 20 / 7
  5. -10 / 3

Write your answers before revealing ours.

Reveal answer
  1. 15 / 4 → 3 (mathematical result 3.75, decimal part removed)
  2. 8 / 3 → 2 (mathematical result 2.666…, decimal part removed)
  3. 3 / 5 → 0 (mathematical result 0.6, decimal part removed)
  4. 20 / 7 → 2 (mathematical result 2.857…, decimal part removed)
  5. -10 / 3 → -3 (mathematical result -3.333…, decimal part removed)

If any answers differed, review the truncation rule: the decimal part is removed, keeping only the whole number portion.


Try it yourself.

Write C# code for this description:

“Create an integer variable named half and bind the result of evaluating total divided by 2 to it.”

Reveal answer
int half = total / 2;

If your answer differed, note what you missed before continuing.


Modulo (Remainder)

Integer division gives us the quotient but discards the remainder. If 7 / 2 gives us 3, what happened to the remaining 1? C# has an operator for this: %, called modulo.

int remainder = 7 % 2;
Console.WriteLine(remainder);

Output: 1

Because 7 divided by 2 is 3 with a remainder of 1.

Modulo: The modulo operator returns the remainder of a division between two integers.

Type signature: modulo: (int, int) → int

ExpressionReasoningResult
7 % 27 ÷ 2 = 3 remainder 11
10 % 310 ÷ 3 = 3 remainder 11
8 % 48 ÷ 4 = 2 remainder 00
5 % 75 ÷ 7 = 0 remainder 55
12 % 512 ÷ 5 = 2 remainder 22

When the first number is smaller than the second, the quotient is 0 and the remainder is the first number itself.

The translation: a % b reads as “a modulo b” or “the remainder when a is divided by b.”

The clock analogy. Think of a 12-hour clock. If it is 10 o’clock and you wait 5 hours, you do not say “15 o’clock.” You say “3 o’clock.” The value wrapped around.

This is modulo: 15 % 12 = 3. Modulo tells you where you land after wrapping around a cycle.

Checking even and odd. A common use of modulo is checking whether a number is even or odd. A number is even if dividing by 2 leaves no remainder:

int n = 7;
int remainder = n % 2;
Console.WriteLine(remainder);

Output: 1

Since the remainder is 1, we know the number 7 is odd. This may be obvious when you can see the number directly, but usually we are checking a variable or the result of a computation. How do I know whether x + y is even or odd? I cannot just look at it. I check the remainder: (x + y) % 2. If the remainder is 0, the result is even. If the remainder is 1, the result is odd. This pattern appears constantly in programming.


Try it yourself.

Evaluate each expression:

  1. 17 % 5
  2. 20 % 4
  3. 3 % 10
  4. 100 % 7

Write your answers before revealing ours.

Reveal answer
  1. 17 % 5 → 2 (17 ÷ 5 = 3 remainder 2)
  2. 20 % 4 → 0 (20 ÷ 4 = 5 remainder 0)
  3. 3 % 10 → 3 (3 ÷ 10 = 0 remainder 3)
  4. 100 % 7 → 2 (100 ÷ 7 = 14 remainder 2)

If any answers differed, work through the division to find the remainder.


Try it yourself.

Write C# code that creates a boolean variable named isEven that is true when n is even and false when n is odd.

Hint: a number is even when n % 2 equals 0.

Reveal answer
bool isEven = n % 2 == 0;

If your answer differed, note what you missed before continuing.


Double Division

Integer division truncates. Double division does not.

double a = 7.0;
double b = 2.0;
double result = a / b;
Console.WriteLine(result);

Output: 3.5

The result keeps its decimal part because doubles can represent decimal values.

Type signature: divide: (double, double) → double

Two doubles go in, one double comes out. No truncation occurs.

ExpressionResult
7.0 / 2.03.5
10.0 / 4.02.5
1.0 / 2.00.5
7.0 / 3.02.333…

Compare to integer division: 7 / 2 gives 3, but 7.0 / 2.0 gives 3.5. The type of the operands determines whether truncation occurs.


Try it yourself.

What does each expression evaluate to?

  1. 9 / 4 (both integers)
  2. 9.0 / 4.0 (both doubles)
  3. 15 / 6 (both integers)
  4. 15.0 / 6.0 (both doubles)

Write your answers before revealing ours.

Reveal answer
  1. 9 / 4 → 2 (integer division, truncated from 2.25)
  2. 9.0 / 4.0 → 2.25 (double division, no truncation)
  3. 15 / 6 → 2 (integer division, truncated from 2.5)
  4. 15.0 / 6.0 → 2.5 (double division, no truncation)

If any answers differed, check whether you identified integer versus double division.


Try it yourself.

Write C# code for this description:

“Create a double variable named half and bind the result of evaluating total divided by 2.0 to it.”

Reveal answer
double half = total / 2.0;

If your answer differed, note what you missed before continuing.


Mixing Integers and Doubles

What happens when an integer and a double appear in the same expression?

int whole = 5;
double fraction = 2.5;
double result = whole + fraction;
Console.WriteLine(result);

Output: 7.5

C# must perform the addition, but the operands have different types. It cannot add an int directly to a double. So C# converts the integer to a double before computing. The calculation becomes 5.0 + 2.5, which evaluates to 7.5.

Promotion: Promotion is when C# automatically converts an integer to a double so that an operation can proceed with matching types. No information is lost because doubles can represent any integer value (within their range).

This matters most with division. Consider:

int a = 7;
int b = 2;
double result = a / b;
Console.WriteLine(result);

Before reading on, predict the output.

Reveal output

Output: 3

The variable is a double, but the result has no decimal part. Let us trace exactly what happens:

  1. Evaluate a → 7 (an int)
  2. Evaluate b → 2 (an int)
  3. Compute 7 / 2 → This is integer division because both operands are integers. Result: 3 (an int)
  4. Now we must store the result in a double. Promote 3 to 3.0
  5. Bind 3.0 to result

The promotion happens after the division, not before. By the time C# promotes the result, the decimal part is already gone.

The translation for double result = a / b; where a and b are integers:

“Create a double variable named result and bind the result of evaluating a divided by b (using integer division, then promoted to double) to it.”

Getting double division from integers. If you want the full decimal result, you must convert one operand to a double before dividing:

int a = 7;
int b = 2;
double result = (double)a / b;
Console.WriteLine(result);

Output: 3.5

The (double)a converts a to a double before the division. Now the calculation is 7.0 / 2. When an operation involves both a double and an integer, the integer is promoted, so this becomes 7.0 / 2.0, which is double division. Result: 3.5.


Try it yourself.

What does each expression evaluate to? Pay attention to the types.

  1. 10 / 4 (both integers)
  2. 10.0 / 4 (double and integer)
  3. 10 / 4.0 (integer and double)
  4. (double)10 / 4 (cast then divide)

Write your answers before revealing ours.

Reveal answer
  1. 10 / 4 → 2 (integer division)
  2. 10.0 / 4 → 2.5 (4 promoted to 4.0, double division)
  3. 10 / 4.0 → 2.5 (10 promoted to 10.0, double division)
  4. (double)10 / 4 → 2.5 (10 becomes 10.0, then 4 promoted, double division)

If any answers differed, identify where promotion occurs and what type of division results.


Try it yourself.

Translate this code to English. Be precise about what happens at each step.

int x = 5;
int y = 3;
double ratio = x / y;
Reveal answer

Line 1: “Create an integer variable named x and bind the value 5 to it.”

Line 2: “Create an integer variable named y and bind the value 3 to it.”

Line 3: “Evaluate x (get 5, an integer), evaluate y (get 3, an integer), divide using integer division (get 1, an integer), promote to a double (get 1.0), and bind to ratio.”

The key insight: the division happens before the promotion, so the decimal part is lost.

If your answer differed, note what you missed before continuing.


Try it yourself.

Write code that computes the average of two integers a and b, storing the result in a double variable named average. The result should include the decimal part.

Reveal answer
double average = (double)(a + b) / 2;

Or:

double average = (a + b) / 2.0;

Either approach ensures double division occurs. The first casts the sum to a double. The second uses a double literal (2.0) which promotes the sum. Both produce the same result.

If your answer differed, check whether your code produces integer or double division.


Precision Limits

Recall that doubles have about 15-16 significant digits of precision. What happens when calculations push against this limit?

double a = 0.1;
double b = 0.2;
double sum = a + b;
Console.WriteLine(sum == 0.3);

Before reading on, predict: does this print True or False?

Reveal output

Output: False

And if we print the actual sum:

Console.WriteLine(sum);

Output: 0.30000000000000004

The sum is not exactly 0.3. It is very close, but not exact.

Why does this happen? A double stores values using a fixed number of digits. When you write 0.1, the computer stores an approximation that fits within those digits. The stored value is extremely close to 0.1, but not precisely 0.1. When you add two approximations together, the tiny errors combine into a visible discrepancy.

Think of it like measuring with a ruler marked only in tenths. If something is 0.1234, you might record it as 0.1. That small rounding usually does not matter, but if you add many rounded measurements together, the errors accumulate.

The practical rule: Do not use == to compare doubles for equality. The tiny precision errors mean two values that should be equal might not be exactly equal.

With integers, == is safe. With doubles, avoid it. We will see better approaches for comparing doubles when we cover functions.


Try it yourself.

For each comparison, predict whether it evaluates to true or false:

  1. 5 == 5 (comparing integers)
  2. 0.1 + 0.1 + 0.1 == 0.3 (comparing doubles)
  3. 10 / 3 == 3 (integer division)
Reveal answer
  1. 5 == 5 → true (integer comparison is exact)
  2. 0.1 + 0.1 + 0.1 == 0.3 → false (precision errors in double arithmetic)
  3. 10 / 3 == 3 → true (10 / 3 truncates to 3, and 3 == 3)

If any answers differed, review the precision rules for doubles.


Comparison Operators

We have computed with numbers. Now we connect numbers back to booleans.

Comparison: A comparison is an operation that takes two values and produces a boolean by checking their relationship.

bool isSmaller = 3 < 5;
Console.WriteLine(isSmaller);

Output: True

The expression 3 < 5 evaluates to true. We now have a boolean we can use in if statements and while loops, just as we did in Chapter 1.

Type signature: lessThan: (int, int) → bool

Two integers go in, one boolean comes out. This is the bridge between numbers and the control flow you learned in Chapter 1.

Here are all six comparison operators:

OperatorNameExampleResult
<less than3 < 5true
>greater than3 > 5false
<=less than or equal5 <= 5true
>=greater than or equal5 >= 5true
==equals5 == 5true
!=not equals5 != 5false

These operators also work with doubles.

The translations:

  • a < b reads as “a is less than b”
  • a > b reads as “a is greater than b”
  • a <= b reads as “a is less than or equal to b”
  • a >= b reads as “a is greater than or equal to b”
  • a == b reads as “a equals b”
  • a != b reads as “a does not equal b”

Let us trace a comparison in context:

int score = 85;
int threshold = 70;
bool passed = score >= threshold;

Line 3 performs these steps:

  1. Evaluate score → 85
  2. Evaluate threshold → 70
  3. Compute 85 >= 70 → true
  4. Bind true to passed

State table:

after linescorethresholdpassed
185
28570
38570true

Try it yourself.

Evaluate each expression:

  1. 10 > 5
  2. 7 <= 7
  3. 4 != 4
  4. -1 >= 0
  5. 3 == 3

Write your answers before revealing ours.

Reveal answer
  1. 10 > 5 → true (10 is greater than 5)
  2. 7 <= 7 → true (7 is equal to 7, and equal counts for “less than or equal”)
  3. 4 != 4 → false (4 does equal 4, so “not equal” is false)
  4. -1 >= 0 → false (-1 is less than 0)
  5. 3 == 3 → true

If any answers differed, review the operator meanings.


Try it yourself.

Translate this code to English:

bool isAdult = age >= 18;
Reveal answer

“Create a boolean variable named isAdult and bind the result of evaluating whether age is greater than or equal to 18 to it.”

If your answer differed, note what you missed before continuing.


Try it yourself.

Write C# code for this description:

“Create a boolean variable named isNegative and bind the result of checking whether balance is less than 0 to it.”

Reveal answer
bool isNegative = balance < 0;

If your answer differed, note what you missed before continuing.


Compound Conditions

A single comparison checks one thing. What if we need to check multiple conditions? We combine comparisons with the boolean operators from Chapter 1: &&, ||, !.

Range check: Is a value between 1 and 100 (inclusive)?

int score = 85;
bool inRange = score >= 1 && score <= 100;

Translation: “score is greater than or equal to 1 AND score is less than or equal to 100.”

Both comparisons must be true for inRange to be true.

Let us trace the evaluation for score >= 1 && score <= 100 where score is 85:

  1. Evaluate score >= 185 >= 1 → true
  2. Evaluate score <= 10085 <= 100 → true
  3. Evaluate true && true → true

Outside a range: Is a value outside the valid range?

int score = 150;
bool invalid = score < 0 || score > 100;

Translation: “score is less than 0 OR score is greater than 100.”

Either comparison being true makes invalid true.


Try it yourself.

Given int x = 25;, evaluate each expression step by step:

  1. x > 0 && x < 50
  2. x < 10 || x > 20
  3. !(x == 25)

Write your answers before revealing ours.

Reveal answer
  1. x > 0 && x < 50

    • 25 > 0 → true
    • 25 < 50 → true
    • true && true → true
  2. x < 10 || x > 20

    • 25 < 10 → false
    • 25 > 20 → true
    • false || true → true
  3. !(x == 25)

    • 25 == 25 → true
    • !true → false

If any answers differed, trace through each step.


Try it yourself.

Write a boolean expression for each description. Assume int n exists.

  1. n is positive (greater than 0)
  2. n is between 10 and 20 (inclusive)
  3. n is either negative or greater than 1000
Reveal answer
  1. n > 0
  2. n >= 10 && n <= 20
  3. n < 0 || n > 1000

If any answers differed, note what you missed before continuing.


Order of Operations

When an expression has multiple operators, C# evaluates them in a specific order. You already know from arithmetic that multiplication happens before addition:

int result = 2 + 3 * 4;  // 14, not 20

The multiplication 3 * 4 happens first (giving 12), then the addition 2 + 12 (giving 14).

The same principle applies to all operators. Here is the order from highest to lowest priority:

  1. ! (not)
  2. *, /, % (multiplication, division, modulo)
  3. +, - (addition, subtraction)
  4. <, >, <=, >= (comparisons)
  5. ==, != (equality)
  6. && (and)
  7. || (or)

This order means comparisons happen before boolean operators. Consider:

bool result = x > 5 && y < 10;

C# evaluates this as (x > 5) && (y < 10), not as some other grouping. The comparisons produce booleans, then && combines them. This matches the natural reading of the expression.

When in doubt, use parentheses to make your intent clear. Parentheses override the default order:

int result = (2 + 3) * 4;  // 20

Now the addition happens first.


From Computation to Control

We now have everything we need to connect numbers to control flow.

In Chapter 1, we wrote conditions using boolean variables:

bool ready = true;
if (ready)
{
    Console.WriteLine("Go!");
}

Now we can write conditions using comparison expressions:

int count = 5;
if (count > 0)
{
    Console.WriteLine("Items remaining");
}

The comparison count > 0 evaluates to a boolean. That boolean controls the branch. The if statement works exactly as before.

Comparisons are the bridge. They take numeric values and produce the booleans that control program flow. In the next section, we will use this bridge to build branching and looping with numbers.


Review

Before continuing, test yourself. Attempt each exercise from memory, then search this section to check your answers.

Definitions:

  1. What is an integer?
  2. What is a double?
  3. What is truncation?
  4. What is overflow?
  5. What is underflow?
  6. What is promotion (in the context of mixed types)?
  7. What is a comparison?

Evaluation:

Evaluate each expression:

  1. 17 / 5
  2. 17 % 5
  3. 17.0 / 5
  4. 10 + 3 * 2
  5. 8 > 5 && 3 < 1
  6. int.MaxValue + 1 (at runtime)
  7. int.MaxValue * 2 (at runtime)
  8. int.MinValue * 2 (at runtime)

Translations:

Translate each line to English:

  1. int half = total / 2;
  2. bool isEven = n % 2 == 0;
  3. double ratio = (double)part / whole;

Writing Code:

Write code for each description:

  1. Create an integer variable named remainder and bind the result of n modulo 7 to it.
  2. Create a boolean variable named inBounds and bind true if position is between 0 and 100 (inclusive), false otherwise.
  3. Create a double variable named average that holds the average of integers a and b, including the decimal part.

State Table:

Complete the state table:

int a = 10;
int b = 3;
int q = a / b;
int r = a % b;
bool divides = r == 0;
after lineabqrdivides
1
2
3
4
5

Check your answers against the examples in this section. Note what you missed and write corrected versions.


You now know how to transform numeric values through computation. You can add, subtract, multiply, divide, find remainders, and compare values. Comparisons produce booleans, connecting numbers to the control flow you learned in Chapter 1.