Чем %f отличается от %lf в scanf и printf
Коротко:
| Тип | scanf | printf |
|---|---|---|
float | %f | %f |
double | %lf | %f или %lf |
long double | %Lf | %Lf |
То есть в scanf буква l обязательна для double, а в printf для double годятся оба варианта.
#include <stdio.h>
int main(void) {
float f;
double d;
scanf("%f %lf", &f, &d);
printf("%f %f\n", f, d);
printf("%.2lf\n", d);
return 0;
}1.5 2.251.500000 2.250000
2.25Почему в scanf есть разница
scanf получает адрес переменной и должен записать туда число в нужном виде. float занимает 4 байта, double — 8, устроены они по-разному. Формат говорит scanf, что именно лежит по адресу. Если прочитать double через %f, в переменную запишутся 4 байта в виде float — получится бессмысленное значение, часто очень маленькое или ноль. gcc с -Wall предупреждает:
warning: format '%f' expects argument of type 'float *', but argument 2 has type 'double *' [-Wformat=]Почему в printf разницы нет
Дробное значение, переданное в printf, по правилам языка всегда превращается в double — даже если переменная была float. Поэтому printf получает только double, и %f для него — правильный формат. Запись %lf в printf стандарт C99 тоже разрешил и сделал равной %f. В очень старых компиляторах, до C99, %lf в printf могла не работать — отсюда советы «в printf только %f».
Другие форматы дробных чисел
%.3f— три знака после точки, с округлением.%e— экспоненциальная запись:1.234500e+03.%g— короче из%fи%e, без лишних нулей:1234.5,1e-07.
#include <stdio.h>
int main(void) {
double x = 1234.5;
printf("%f | %.1f | %e | %g\n", x, x, x, x);
printf("%g\n", 0.0000001);
return 0;
}1234.500000 | 1234.5 | 1.234500e+03 | 1234.5
1e-07Частые ошибки
- Ввод через запятую.
scanfждёт точку: при вводе2,5прочитается только2, а,5останется непрочитанным. %dдля дробного числа.printf("%d", 3.5)выводит мусор, а не 3. Чтобы вывести целую часть, нужно приведение:printf("%d", (int)3.5).- float вместо double. У
floatоколо 7 значащих цифр, и ошибки округления видны уже в простых расчётах. Если нет особой причины, беритеdouble.