9

The Wikipedia page on PDP-11 architecture has a very interesting bullet point in the section on the Floating Point Processor extension to the basic architecture:

  • full floating point operations on single- or double-precision operands, selected by single/double bit in Floating Point Status Register

If a program did arithmetic on both float and double variables, without mixing them in an expression, it would be possible for the compiler to generate code for setting and clearing the single/double bit while the program was running, but it would be awkward, and would slow the program down.

Did the first implementers of C, working on a PDP-11, simply decide to keep the bit set to double at all times? That would force arithmetic on floats to be done in double precision, which C explicitly allows, but does not require.

The original Bell System Technical Journal article on C, which describes the PDP-11 implementation, mentions that all arithmetic on floats is done using doubles but does not say why.

The Unix second edition manual is from 1972, before the PDP 11/45 with floating point arrived. It describes FPTRAP (III), the emulator package for 11/45 floating point that Second Edition used on PDP-11s without FP hardware. That section makes it clear that the decision to do all FP arithmetic in double precision had already been taken, but again, does not say why.

11
  • 1
    For what it's worth, the decision was made before the 11/45 actually arrived, but obviously after studying the 11/45 handbook. See 2nd Edition manual, FPTRAP (III), which describes the FP emulation facilities.
    – dave
    Nov 25, 2022 at 20:37
  • 3
    To me it seems Very Sensible to do floating point computations in on-chip registers of one width, and store/retrieve from memory at another width, even supporting a few different formats in memory but using always the same width for actual computation. You could consider for example the 8087 which does just that. And earlier machines as well do just that, such as the IBM 704 or the English Electric KDF-9, I believe also the PDP-7 which these guys were obviously familiar with Nov 25, 2022 at 23:20
  • KDF9 supported two floating-point sizes, at least as far as the programmer was concerned. +F is single-precision floating point add (N1 := N1 + N2), +FD is double-precision (N1,N2 := N1,N2 + N3,N4).
    – dave
    Nov 26, 2022 at 13:53
  • Did the PDP-7 have hardware floating point?
    – dave
    Nov 26, 2022 at 14:19
  • @another-dave (KDF-9) but on the nest (the FP stack), the values had all the same double precision, isn't that right? Nov 26, 2022 at 18:31

1 Answer 1

6

A fundamental part of "1974 C"'s simplicity, which unfortunately fell by the wayside as the language evolved, was that all operations other than loads and stores involved one of four data types: integers, floating-point numbers, object pointers, and function pointers. If compilers or programmers encountered a function call like:

int myFunction();
int x;

test(4, 1.0, &x, myFunction);

they could know the precise types of the arguments that test should be expecting, even if they knew nothing about test and myFunction beyond the fact that the latter was a function returning int.

Having computations like float1 = float2 + float3; would have required that the logic include logic to handle the addition of float values, in addition to the logic that would likely be needed elsewhere to add double values, convert float to double, and convert double to float. By contrast, having all float values converted to double as soon as they're read allows compiler logic to be simplified.

Additionally, though I don't know if this was a consideration when Ritchie was first implementing C, this design also facilitated software floating-point implementations, at least until long double was added in a manner that broke it. Processing a packed floating-point number without floating-point hardware generally requires performing a bunch of steps to unpack all of the bits into separate parts for the exponent, sign, and mantissa. If an implementation for the Motorola 68000 were to represent its double type using a 64-bit mantissa without an implied 1, along with a 16-bit value that held the exponent and sign, the time required to perform a computation like a-b+c in cases where all values were of similar magnitude may be less than the costs of converting the intermediate results to/from an IEEE-754 32-bit single-precision value or worse, an IEEE-754 64-bit double-precision value. The documentation for the Standard Apple Numerical Environment used on the Macintosh strongly recommended using 80-bit extended-precision type for most compuations, and minimizing the use of IEEE-754 64-bit type, for this reason.

Having different sizes of integer or floating-point values would have made the language much harder to work with in the days before function prototypes. Under 1974 rules, the function calls foo(v * 1.1f) and foo(v * 1.1) will both pass their argument the same way, unaffected by whether v is float or double. If the program wants the value scaled by the numerical value 1.1, the scaling constant can be written as 1.1 without having to worry about whether the function might be expecting a float.

The addition of integer types whose ranges go beyond INT_MIN and INT_MAX, as well as the long double type could have been made less disruptive if there were a means by whcih variadic functions could indicate what type of integer and what type of floating-point value they wanted, and arguments that were not expressly cast into some other type would be coerced to the indicated form. Thus, for example, something like:

printf("%d %ld", x, (long)y)

would be able to handle out any integer value within the range of int stored in x, regardless of its type, and would correctly process any numeric value in y, again regardless of type. Code wanting to format long values would need to include explicit casts in the argument expressions, but code where the format string matched the arguments would continue to do so even if objects of some integer types were replaced by objects of other integer types, and likewise for objects of floating-point types.

7
  • I'm not sure I follow this as far as an answer to the question. Are you saying that the distinction between float and double would only be meaningful when fetching or storing values from typed variables? And thus, floating-point values held in registers, stored in compiler-created temporary variables, or being passed to functions would always be doubles? Nov 27, 2022 at 19:25
  • I think that was the design. The compiler might have included code to avoid float-double-float conversions in simple constructs like x=y, but "All floating arithmetic in C is carried out in double-precision; whenever a float appears in an expression it is lengthened to double by zero-padding its fraction. When a double must be converted to float, for example by an assignment, the double is rounded before truncation to float length."
    – supercat
    Nov 27, 2022 at 19:33
  • @JohnDallman: See above. Floating-point code often involves time/space trade-offs, and for a range of typical FP library code space budgets, unless a program never needs anything beyond float precision, eliminating float routines for most kinds of math and using the saved space to accommodate faster double routines would often yield a performance win--despite the higher precision of the computations--if programs avoided conversion except when compact storage was needed.
    – supercat
    Nov 27, 2022 at 19:43
  • @JohnDallman: Note that the design is only good if double format is designed to maximize computation speed over compactness, and is not good when using something like an IEEE-754 double. The IEEE-754 types could have best been accommodated on many machines by using "short double" as the name for the 64-bit floating-point type, and keeping double as the name for the longest precision type.
    – supercat
    Nov 27, 2022 at 19:47
  • The x87 had 80-bit hardware, as did the 68000 FPUs, but I don't know of any more. Do you have examples? Nov 29, 2022 at 11:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .