cint

cint university / 101 / ced, the cint editor

101-05: when a number runs out

This is lesson 101-05 of CU-101.2, checked against cint 0.2.0.

You will double a number until it no longer fits its box, watch cint stop at that exact step and read the report it prints.

the program

// Double a number until it no longer fits in a U8.
U8 x = 1;
for step in 1..=10 {
    "step {step}: {x}\n";
    x *= 2;
}

Run this program in ced

The program has two new parts. for step in 1..=10 { ... } is a loop: it runs the lines between the braces ten times, with step counting 1, 2, 3 and so on up to 10. Lesson 101-07 is all about loops. x *= 2; doubles x, the way += added in lesson 101-03.

Run it:

step 1: 1
step 2: 2
step 3: 4
step 4: 8
step 5: 16
step 6: 32
step 7: 64
step 8: 128
fault[E_OVERFLOW]: the exact result is outside the type's range
  --> doubling.ci:5:7
   |
 5 |     x *= 2;
   |       ^ mul.checked.u8
   |
   = operation: mul.checked.u8
   = operand-count: 2
   = operand: U8 128
   = operand: U8 2
   = exact:   256
   = limit:   U8 255
   = revision: 8efe88d0637573b3291746f4096933013c3e869ecc8be5ae436c45314575f6f4
   = source-map: none
   = address: none
   = stack-depth: 0
   = fuel-consumed: 9 (not part of the fault record)
   = state: the program stopped; stdout holds what it wrote before the fault

what happened

Eight lines printed. At step 8, x held 128, and the next doubling would make 256. The largest U8 is 255. There is no right answer that fits, so cint stopped the program at that multiplication and told you what happened. This is called a fault. Lesson 101-01's errors stop a program before it runs; a fault stops it while it runs, at the exact step that went wrong.

reading the fault report

The report is long, but five parts of it explain the fault:

Line What it tells you
fault[E_OVERFLOW]: the exact result is outside the type's range What kind of fault. Overflow means the true answer is too large (or too small) for the type.
--> doubling.ci:5:7 and the picture Where: line 5, character 7, the *=.
operation: mul.checked.u8 What was being done: a checked multiplication (mul) of two U8 numbers.
operand: U8 128 and operand: U8 2 The numbers it was working on. Operands are the numbers an operation takes.
exact: 256 and limit: U8 255 The true answer, and the most the type can hold.

The other lines are there for tools that read faults, and you can skip them. revision is a fingerprint of the program's operations. fuel-consumed counts the steps the program took to run, which are not the loop's step. stack-depth (lesson 101-11 uses it), source-map and address matter in larger programs.

Notice what did not happen: the program did not print a wrong number. Everything it printed before the fault is true.

the same fault, every time

Run the program again, and again. You get the same eight lines and the same report, with the same numbers in it, every time. Run it on another computer and the output is the same down to the last character. In your browser the report is laid out differently, but it names the same fault, the same operation and the same numbers.

A mistake that shows up every time, at the same place, with the numbers that caused it written down, is a mistake you can find and fix. It also means that you can run the old and new versions of a changed program and compare them line by line. Any difference then comes from your change.

fix it

There are two ways to fix a program that runs out, and each one means something different:

  1. A bigger box: change U8 to U16 in the line U8 x = 1;. Now all ten steps fit.
  2. Fewer steps: the doubling at the end of step 8 is the one that does not fit. If the program only needs the first seven numbers, change 1..=10 to 1..=7. The last doubling is then 64 times 2, and 128 fits.

change it

  1. Make the box U16 and the loop 1..=20, and delete the comment line. Before you run it: at which step will it stop, and what will exact and limit say?
  2. Triple instead of double, with x *= 3;. Which step stops a U8 now?
where the U16 version stops
step 16: 32768
fault[E_OVERFLOW]: the exact result is outside the type's range
  --> doubling.ci:4:7
   ...
   = operand: U16 32768
   = operand: U16 2
   = exact:   65536
   = limit:   U16 65535
(The line number is 4 because the comment line is gone.)

Two other programming languages, C and Python, behave differently. The same loop in C, with an 8-bit unsigned x, built with GCC 13.3 on Linux with no options, prints step 9: 0 and step 10: 0 and gives no sign that anything went wrong. Python's whole numbers have no fixed limit, so it prints 256 and 512.

when it goes wrong

Lesson 101-04's as is checked the same way. Moving 300 into a U8:

I64 big = 300;
U8 small = big as U8;

Run this program in ced

stops with a different fault:

fault[E_NARROW]: the value does not fit the destination type
  --> narrow.ci:2:16
   |
 2 | U8 small = big as U8;
   |                ^ as.checked.i64.u8
   |
   = operation: as.checked.i64.u8
   = operand-count: 1
   = operand: I64 300
   = exact:   300
   = limit:   U8 255
   ...

Narrow means moving a number into a smaller type. The same five parts describe the fault: what kind of fault, where, which operation, the number it had and the limit it hit.

new words

fault, overflow, operand, fault report, loop

Next: lesson 101-06, true or false.