← board

Pascal Single + Single is typed Double

Measured, FPC as oracle

var a, b: Single; d: Double;
begin
  a := 0.1; b := 0.2; d := a + b;
  WriteLn(d:0:9);
  WriteLn(SizeOf(a + b));
end.
pxx fpc -Mobjfpc
d := a + b 0.300000004 0.300000012
SizeOf(a + b) 8 4

0.300000012 is the single sum; 0.300000004 is the double sum widened.

Why the C fix does not port

The C ticket's own summary is the discriminator, and it is worth stating because the two look like one defect:

So the first question here is the TYPE RULE, not the width. Settle what Single + Single should be typed as, and check what depends on the current answer before changing SizeOf from 8 to 4 — that is an observable a program can branch on.

Not started

Filed with the measurement only. The C half is fixed and tested (test/c_float_arith_at_single_width.c, gcc oracle, 17 rows, verified byte-identical on x86-64, i386, aarch64, arm32 and riscv32).

KNOWN DIVERGENCE 2026-09-02 — the measurement is right, the conclusion is not

This ticket asked the right question — "settle the TYPE RULE" — and the owner has now settled it: Single + Single evaluating at double width is correct, and a programmer who wants the narrow type casts.

Re-measured at 1d3da6ae9, compiler sha256 468194333634 (converged after 2 round(s), so a real rebuild and not the stamp path):

d := a+b     -> 0.300000004      (Double target: the double sum, MORE accurate)
s := a+b     -> 0.300000012      (Single target: FPC's exact answer)
SizeOf(a)    -> 4                (agrees with FPC)
SizeOf(a+b)  -> 8                (FPC: 4)
P(a+b)       -> picked DOUBLE    (FPC: picks Single)
P(a)         -> picked SINGLE

The row that decides it is the second one. Store the sum in a Single and you get 0.300000012 — byte-for-byte FPC's answer. No program gets a wrong value. The only way to reproduce FPC's 0.300000012 in a Double target is to round to single precision and then widen, i.e. to deliberately throw away precision we already have, in order to match another compiler's rounding. That is FPC-parity chasing, which CLAUDE.md rules out: on par with the LANGUAGE, not with FPC.

Correcting the record on SizeOf

The disposition does NOT rest on "SizeOf reports it right". It does for the variable (SizeOf(a) = 4) and it does not for the expression (SizeOf(a+b) = 8, where FPC says 4). Stating the reason accurately matters, because the wrong reason invites a refile the first time someone runs the expression form.

Two CHOSEN behaviours — and SizeOf was not diverging, it was working

Recorded as chosen, not tolerated (owner, 2026-09-02). Neither compiler is wrong here and neither is more right:

  1. SizeOf(a+b) = 8 is TRUE. It is a correct statement about the size of a pxx expression, exactly as FPC's 4 is a correct statement about an FPC one. Each reports its own compiler's representation faithfully — "the programmer had all information it wants; sizeof reported CORRECTLY about the accurate type. that's why it exists — to not make assumptions." A programmer who asks rather than assumes is served correctly by both compilers. A truthful instrument returning an answer you did not expect is not a defect, and this ticket read one as a defect because it expected the other number.

  2. Overload resolution picks the Double arm, because the argument IS a double: Single is a storage type and double is the native evaluation type. The call is consistent with the expression's actual type rather than in spite of it.

The distinction between chosen and tolerated is the point of writing this down. "We accept this divergence" concedes something was off and invites the next reader to re-litigate it. "Both answers are correct about different representations" closes it.

A caller who wants the narrow static type writes Single(a + b) — ordinary Pascal, not a workaround.

What would reopen this

Real source — not a probe — that is CORRECT under FPC and wrong under pxx because of the expression's static type. Over-allocation and a more precise overload do not qualify. Absent that, this stays closed.

The C half (bug-c-float-plus-float-is-computed-at-double-width) remains correctly fixed and is untouched by this: there the node was already tagged tySingle, so its evaluation width contradicted its own declared type. That is an internal inconsistency, which is a real defect. Pascal has no such contradiction — the node says Double and evaluates as Double.