The C standard specifies that by default floating point constants should be held as double precision values. Calculations where one variable is single precision and one variable is double precision need to be computed as double precision. So it's very easy to have a code which works entirely on single precision variables, but ends up with lots of conversion between single and double precision because the constants are not specified as single precision. An example of this is shown in the next code snippet.
float f(float a)
% cc -O -S ff.c
/\* 0x0008 \*/ ldd [%o5+%lo(offset)],%f4
/\* 0x000c \*/ ld [%sp+68],%f2
/\* 0x0010 \*/ fstod %f2,%f6
/\* 0x0014 \*/ fmuld %f6,%f4,%f8
/\* 0x0018 \*/ retl ! Result = %f0
/\* 0x001c \*/ fdtos %f8,%f0
The fstod instruction converts the constant variable a from single precision to double precision, the fdtos converts the result of the multiplication back to single precision.
The option to specify that unsuffixed floating point constants are held as single precision rather than the default of double precision is -xsfpconst. This option works at file level, so is not ideal for situations where files are a mix of single and double precisions constants. This flag can cause the opposite problem where the single precision constant needs to be converted to double precision in order to be used in a calculation with a double precision variable.
The issue is easy to work around at a source code level. Single precision constants can be specified with a trailing f. For example:
float f(float a)
Compiling this code produces the following much neater and more efficient code:
/\* 0x0008 \*/ ld [%o5+%lo(offset)],%f2
/\* 0x000c \*/ ld [%sp+68],%f4
/\* 0x0010 \*/ retl ! Result = %f0
/\* 0x0014 \*/ fmuls %f4,%f2,%f0