Values That Move, Split, and Get Measured

Week 2 has four short programs and every one of them is about what happens to a value in transit. Three variables rotate through each other, which is the swap from chapter 3 with one more step. A real number is cut into its whole and fractional parts, which is the double-to-integer conversion from chapter 3 doing exactly what the standard says and nothing more. And sizeof reports how much room each type takes, which is chapter 2 turned into a printout. Nothing beyond chapters 2 to 4 is needed.

Program 1: Rotate Three Values

Given x, y, and z, make x take the value of y, y take the value of z, and z take the value of x. Print the three before and after.

Algorithm:

  1. Start.
  2. Read x, y, and z.
  3. Print the three values.
  4. Save x in a temporary variable.
  5. Assign y to x.
  6. Assign z to y.
  7. Assign the saved value to z.
  8. Print the three values again.
  9. Stop.

Run it with 1 2 3 and the second line reads x = 2, y = 3, z = 1. Now delete temp, replace the last assignment with z = x;, and run it again: z comes out as 2, because by the time that line runs x already holds the old y. The chapter 7 bubble sort needed a temporary to swap two values; a rotation of three needs the same one temporary, and only one, because after x is saved every other value has a free slot to move into. Trace it on paper with the three boxes and an arrow for each assignment; that trace is what an examiner asks for in the viva.

Program 2: The Right-Most Digit of the Integral Part

Read a real number such as 123.45 and print the last digit of its whole part, here 3.

Algorithm:

  1. Start.
  2. Read the real number.
  3. Convert it to a whole number, discarding the fraction.
  4. Take the remainder on division by 10; if it is negative, negate it.
  5. Print the digit.
  6. Stop.

123.45 gives 3, and 9.99 gives 9, not 0: the cast to long truncates toward zero, it never rounds, so 9.99 becomes 9. That is the whole reason the cast is the right tool here and a rounding function is not. -56.7 gives 6 only because of the sign fix: the cast produces -56, and week 1's rule says -56 % 10 is -6, so the program negates a negative remainder before printing.

One thing the standard says and the lab manual does not: converting a double to an integer type is undefined behaviour when the whole part does not fit in that type. A long is guaranteed at least 32 bits, and on this platform it is 64, so any number you would type at a keyboard is safe; a program that reads values from a file it does not control would need to check the range before casting.

Program 3: Integral and Decimal Parts

Read a real number and print its whole and fractional parts separately: 123.45 becomes 123 and 0.45.

Algorithm:

  1. Start.
  2. Read the real number.
  3. Convert it to a whole number to get the integral part.
  4. Subtract the integral part from the number to get the decimal part.
  5. Print both parts.
  6. Stop.

The subtraction on step 4 mixes a double and a long, and chapter 3's conversion rule promotes the long to double, so decimal is a real number. For -12.75 the parts come out as -12 and -0.75: both carry the sign, and that is the honest answer, since the two parts must add back up to the original.

Change %.2f to %.20f and run 123.45 again. The decimal part prints as something like 0.45000000000000284217, not 0.45, because 123.45 has no exact binary representation and the subtraction exposes the error that was there all along. %.2f is not hiding a bug; it is the correct amount of precision to claim for a number that was never exact. The library function modf in <math.h> does this split in one call and is a good answer to the viva question "is there another way".

Program 4: Sizes of the Data Types

Print the size in bytes of each basic type.

Algorithm:

  1. Start.
  2. For each type in turn, compute its size with sizeof.
  3. Print the type's name and its size.
  4. Stop.

sizeof yields a value of type size_t, an unsigned type whose exact identity the platform chooses. C99 added %zu to print it directly; C89 has no such specifier, so the portable C89 form is the one above: cast to unsigned long and print with %lu. Chapter 2 stored the result in an unsigned long variable first, which is the same idea in two statements.

What the output means is the exam question. On this platform int is 4 and long is 8. Those are facts about this compiler, not about C: the standard guarantees only minimums, char exactly 1 byte by definition, short and int at least 16 bits, long at least 32. The textbook's tables, written for 16-bit Turbo C, say int is 2, and an answer that says "int is 2 bytes" or "int is 4 bytes" without naming the platform is wrong either way. The sentence to write is that sizeof(int) is implementation-defined, and this program is how you find out.

Key Takeaways

  • Rotating three values takes one temporary and three assignments, saved value first, in the order that leaves each slot free before it is written.
  • Casting a double to an integer type truncates toward zero, never rounds; it is undefined behaviour if the whole part does not fit.
  • The remainder of a negative number is negative, so extracting a digit from a possibly negative value needs a sign fix.
  • Subtracting the truncated whole part from a double leaves the fraction, with the same sign as the original and the binary error %.2f sensibly hides.
  • sizeof yields size_t; in C89 print it as (unsigned long) with %lu.
  • Type sizes are implementation-defined: the standard fixes minimums, not widths, and the program is the only way to learn what a platform chose.