Oracle Java Foundations study materials
Oracle Java Foundations study resources

The Payroll Average That Lost Its Fractions

Oracle Java Foundations · Put your knowledge to work in context.

All case studies

The Payroll Average That Lost Its Fractions

A payroll module keeps headcount and totals as int. With int total = 17; int count = 4; the line double avg = total / count; stores 4.0, and the printed report shows 4.00 where the analyst expects 4.25. A separate cleanup changed several x = x + n; statements into compound form. After the cleanup, byte flags = 8; flags = flags + 1; and short q = 100; q = q * 2; fail to compile, while a reviewer's version using flags += 1; and q *= 2; compiles and runs. The team lead wants both symptoms explained, plus a note on what could go wrong later, before the release branch is cut.

1. Explain why `avg` holds 4.0, give two code changes that yield 4.25, and identify a change that looks correct but is not.

Read the worked answer

Both operands of / are int, so binary numeric promotion leaves them as int and integer division runs: 17 / 4 produces the int value 4, truncating toward zero. The widening to double happens only at the assignment, so 4.0 is stored and the fraction is already gone.

Correct fixes - make at least one operand floating point before the division:

double avg = (double) total / count;   // 4.25
double avg = total / (double) count;   // 4.25
double avg = 1.0 * total / count;      // 4.25

The trap is (double) (total / count). The cast is applied to an already-computed int, so it converts 4 to 4.0 and the report is still wrong. Casting must reach an operand, not the finished quotient.

2. Why do the compound-assignment versions compile when the long forms do not, and what defect can that hidden conversion still cause?

Read the worked answer

Compound assignment E1 op= E2 is specified as E1 = (T) ((E1) op (E2)), where T is the type of the left-hand side. The compiler inserts the narrowing cast implicitly, so flags += 1 is really flags = (byte)(flags + 1) and is legal. The long form flags = flags + 1 promotes byte to int, and assigning an int back to a byte variable requires an explicit cast, so it is a compile-time error. Same story for short.

The residual defect is silent truncation. The cast discards high-order bits without any warning:

byte b = 127;
b += 1;        // b is now -128
short q = 20000;
q *= 2;        // q is now -25536, not 40000

No exception is thrown, so a value that only occasionally exceeds the type range corrupts data quietly. Range checks or a wider type must be chosen deliberately.

3. Predict the values of `int a = 5; double d = a / 2 * 2.0;` and `double e = a / 2.0 * 2;` and state the rule you applied.

Read the worked answer

Evaluation is left to right with each operator obeying its own operand types.

  • a / 2: both int, so integer division gives 2. Then 2 * 2.0 promotes to double, giving d == 4.0.
  • a / 2.0: the double operand promotes a, so this is 2.5. Then 2.5 * 2 gives e == 5.0.

The rule: a fraction survives only if a floating-point operand is present at the moment the division executes. Placement of .0 to the right of the / cannot rescue a division that already ran on two ints.