Is there a printf specifier that requires float not double?
Asked Answered
E

4

8

I'm getting MISRA type errors when I use "%f" specifier for snprintf with a parameter of type float.

According to my research, MISRA is correct because "%f" expectes a type of double.

Is there a floating point specifier or modifier that will use a float type parameter and not a double?

I'm working on an embedded system and don't want to convert from 32-bit float to 64-bit double just to please the snprintf function. The code prints to the debug / console port and this is the only place where the conversion takes place.

For those of you needing a code example:

// This section is for those C++ purists and it fulfills the C++ tag.
#if __cplusplus
#include <cstdio>
#else
#include <stdio.h>
#endif

#define BUFFER_SIZE (128U)

int main(void)
{
    char buffer[BUFFER_SIZE];
    float my_float = 1.234F;

    // The compiler will promote the single precision "my_float"
    //   to double precision before passing to snprintf.
    (void)snprintf(buffer, BUFFER_SIZE, "%10.4f", my_float);
    puts(buffer);
    return 0;
}

All my research on SO and the Web is about printing a floating point value, not about which specifiers will require a float parameter so that no promotion to double takes place.

I am using IAR Embedded Workbench compiler for ARM7TDMI processor.

Evasive answered 9/1, 2013 at 1:38 Comment(4)
There is no rule in MISRA-C:2004 related to type specifiers of the *printf functions. The only MISRA rule that applies is 20.9 "stdio.h shall not be used in production code". So it would seem that your compiler gives the wrong error, it should tell you "Error: use of forbidden function" or something like that.Norine
And there is also 16.1 "Functions shall not be defined with variable numbers of arguments" which will effectively ban stdio.h as well.Norine
Why does printf() promote a float to a double?Scene
In general, embedded processor compilers often have options to override standard language behaviour and use float representation for double everywhere in the program, eliminating the problematic conversion. I have no idea whether that particular compiler you use has one.Gillespie
B
21

No, because printf and its friends are variadic functions, so a float parameter undergoes automatic conversion to double as part of the default argument promotions (see section 6.5.2.2 of the C99 standard).

I'm not sure why this triggers a MISRA warning though, I can't think of any way in which this might be dangerous.

Brebner answered 9/1, 2013 at 1:40 Comment(9)
Is there any way to prevent or stop the default argument promotion?Evasive
@ThomasMatthews: Nope. Your best bet, unfortunately, is to make your own version of sprintf that has a float specifier, and pass that float as a char* then reinterpret it inside your printing function.Resource
@ThomasMatthews: Does MISRA warn about float f; printf("%f", (double)f);? The cast is redundant as far as C is concerned, but perhaps it will make MISRA happy. -- Oh, wait, you said you didn't want to do the conversion.Barter
@KeithThompson: The MISRA complaint will go away if I explicitly state the conversion to double. My point is that I don't want to promote from a 32-bit quantity to a 64-bit, just to please a function. I'm using float rather than double due to space concerns in an embedded system; also, the promotion hinders performance (although probably negligle with the UI / Console).Evasive
@ThomasMatthews: Considering what's actually involved in going from float to double, the conversion is probably trivial, even on a platform without hardware floating-point support.Brebner
At least, compared to the conversion to a decimal representation, it's trivial.Largeminded
@ThomasMatthews: You can pass a float argument to a function without having it automatically promote to double -- as long as the function isn't variadic. You could write your own function that takes a float argument and does what snprintf(buffer, BUFFER_SIZE, "%10.4f", my_float); does. I seriously doubt that the performance would be significantly better, but can try it and measure it yourself.Barter
@ThomasMatthews Your static analyser is broken, it should not let you use stdio.h nor variable argument lists in the first place. There are no rules in MISRA-C regarding type specifiers, because stdio.h can never be used in a MISRA-compliant program. The IAR MISRA-checker is infamous for giving completely nonsense errors, but then so is pretty much every other MISRA checker on the market as well..Norine
As for this discussion, there is never a case in MISRA compliant code where the default argument promotions will appear.Norine
S
9

No, there's not, because the standard promotions convert every float argument into a double when passed through a variable parameter list.

Shawana answered 9/1, 2013 at 1:40 Comment(0)
N
5

Correct MISRA-C:2004 compliance analysis should give:

  • Violation of 1.1, code does not conform to ISO 9899:1990 (C++ code, C99 code).
  • Violation of 2.2, use of // comment.
  • Violation of 16.1, use of variable argument functions.
  • Violation of 20.9, use of stdio.h.

If you get other errors than those above, your static analyser might be broken.

I have analysed manually, as well as with LDRA Testbed 7.6.0.

Norine answered 9/1, 2013 at 13:59 Comment(2)
If using stdio.h is a violation, it appears difficult to convert a double to a string.Fouquiertinville
@chux Yes. You'd have to use some other library or roll out your own. On the other hand, a custom function will be much faster than sprintf. Or you could raise a deviation from MISRA and go with stdio.h. Though in my experience, many programs that use float could and should be rewritten to use plain integers instead. Beginners tend to make the decision to use float based on whether the output needs a decimal point or not, which is of course nonsense. Only programs that need more advanced math actually need float.Norine
N
0

it's not possible specify float instead of double in printf functions because of automatic promotion, but I think you can change your code from:

(void)snprintf(buffer, BUFFER_SIZE, "%10.4f", my_float);

to:

(void)snprintf(buffer, BUFFER_SIZE, "%10.4f", (double)my_float);

and achieve the right result

Neilson answered 25/8, 2015 at 10:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.