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:
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
myFunction beyond the fact that the latter was a function returning
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
double, and convert
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
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
The addition of integer types whose ranges go beyond
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.
+Fis single-precision floating point add (N1 := N1 + N2),
+FDis double-precision (N1,N2 := N1,N2 + N3,N4).