5

There are new format specifiers for intN_t types, for example %"PRIiN" and %"SCNiN", for printf and scanf families of functions.

What are, if any, the new format specifiers for float_t and double_t? (defined in math.h)

Can I -safely- use %f and %lf? I don't think so, because float_t is only at least as large as float, but could be defined as long double.

As nobody answered, and I don't find the answer anywhere, may it be a bug in C?

  • So? Hint: look up what kind of type `printf("%f")` actually expects, then read the C11 standard about function calls and be enlightened. – EOF Feb 17 '18 at 19:25
  • @EOF Ok, so printf doesn't care which type (float, double, float_t...) it is because it casts it to a double (Does it cast it even when it is a long double?). But scanf doesn't, so half of the question still makes sense to me. Thanks for that. I wanted to know that for a long time. – alx - recommends codidact Feb 17 '18 at 19:45
  • 4
    As `printf("%f")` doesnt cast a `long double` to a `double`, it could happen that `float_t` was defined as a `long double`, which wouldn't work if `%f` was used. Therefore, a macro like `PRIiN` should exist for that. – alx - recommends codidact Feb 17 '18 at 19:59
  • @CacahueteFrito: The `long double` type would have been much more usable if it would convert to `double` when passed to a variadic function without being wrapped in an explicit "pass as long double" wrapper. C was designed on the assumption that floating-point values of *all* types would be converted to a common type when passed as arguments, and the design of `long double` broke that. The only way to make a lot of code work was to treat `long double` as a synonym for `double` even on systems which performed computations using the extended precision type that was... – supercat Jun 05 '18 at 20:58
  • ...both faster and more accurate than `double`. Such horrible semantics led to people blaming extended-precision types for calculation problems that are really the fault of the language. – supercat Jun 05 '18 at 20:59

2 Answers2

1

To be cautious and portable you could us %Lf in your printf control string and cast values of type float_t or double_t to long double.

Edited: Format specifier for long double is %Lf and not %lf

You have to be more cautious for scanf because in that case casting won't help you.

Alternatively you could define your own printf and scanf format specifiers for float_t and double_t, making use of the FLT_EVAL_METHOD[1] macro to find out which built-in types that float_t and double_t are respectively equivalent to.

0

Those don't exist in the Standard, so you have to define them yourself making use of FLT_EVAL_METHOD (C11 7.12.2).

I will share the code I wrote in my library to do so:

#if (FLT_EVAL_METHOD == 0)
#define PRIaFLT     "a"
#define PRIAFLT     "A"
#define PRIeFLT     "e"
#define PRIEFLT     "E"
#define PRIfFLT     "f"
#define PRIFFLT     "F"
#define PRIgFLT     "g"
#define PRIGFLT     "G"

#define SCNaFLT     "a"
#define SCNeFLT     "e"
#define SCNfFLT     "f"
#define SCNgFLT     "g"

#define PRIaDBL     "la"
#define PRIADBL     "lA"
#define PRIeDBL     "le"
#define PRIEDBL     "lE"
#define PRIfDBL     "lf"
#define PRIFDBL     "lF"
#define PRIgDBL     "lg"
#define PRIGDBL     "lG"

#define SCNaDBL     "la"
#define SCNeDBL     "le"
#define SCNfDBL     "lf"
#define SCNgDBL     "lg"


#elif   (FLT_EVAL_METHOD == 1)
#define PRIaFLT     "la"
#define PRIAFLT     "lA"
#define PRIeFLT     "le"
#define PRIEFLT     "lE"
#define PRIfFLT     "lf"
#define PRIFFLT     "lF"
#define PRIgFLT     "lg"
#define PRIGFLT     "lG"

#define SCNaFLT     "la"
#define SCNeFLT     "le"
#define SCNfFLT     "lf"
#define SCNgFLT     "lg"

#define PRIaDBL     "la"
#define PRIADBL     "lA"
#define PRIeDBL     "le"
#define PRIEDBL     "lE"
#define PRIfDBL     "lf"
#define PRIFDBL     "lF"
#define PRIgDBL     "lg"
#define PRIGDBL     "lG"

#define SCNaDBL     "la"
#define SCNeDBL     "le"
#define SCNfDBL     "lf"
#define SCNgDBL     "lg"


#elif   (FLT_EVAL_METHOD == 2)
#define PRIaFLT     "La"
#define PRIAFLT     "LA"
#define PRIeFLT     "Le"
#define PRIEFLT     "LE"
#define PRIfFLT     "Lf"
#define PRIFFLT     "LF"
#define PRIgFLT     "Lg"
#define PRIGFLT     "LG"

#define SCNaFLT     "La"
#define SCNeFLT     "Le"
#define SCNfFLT     "Lf"
#define SCNgFLT     "Lg"

#define PRIaDBL     "La"
#define PRIADBL     "LA"
#define PRIeDBL     "Le"
#define PRIEDBL     "LE"
#define PRIfDBL     "Lf"
#define PRIFDBL     "LF"
#define PRIgDBL     "Lg"
#define PRIGDBL     "LG"

#define SCNaDBL     "La"
#define SCNeDBL     "Le"
#define SCNfDBL     "Lf"
#define SCNgDBL     "Lg"
#endif

And then you should be able to do:

double_t    x;

scanf(" %"SCNaDBL"", &x);
printf("x = %"PRIaDBL"\n", x);
  • 1
    If code used the `l` and `L` directly as in the last `#define SCNdbl "Lf"`then `FLOAT_PREFIX, DOUBLE_PREFIX, LONG_DOUBLE_PREFIX` could be eliminated, reducing unnecessary name-space impact. – chux - Reinstate Monica Mar 31 '19 at 14:23