Задача из E-Olymp:Площадь и объем пирамиды
#include <stdio.h>
#include <math.h>
int main(void)
{
float p,d,H,h,S,V;
scanf("%f %f",&d,&p);
S=d*d + 2*d*sqrt(p*p-(d*d)/4);
H=sqrt(p*p - (d*d)/2);
V=d*d*H/3;
printf("%.3f %.3f",S,V);
}
Много раз перепроверял,ошибку не вижу вообще.Помогите!
Ответы (2 шт):
Исправьте float на double и вы будете приятно удивлены.
И вообще, в общем случае при вычислениях с плавающей точкой используйте double, а не float.
Оценим может ли тип float выдать нужную точность в этой задаче. В условиях сказано что сторона основания и длина бокового ребра не превосходят 100. Тогда площадь поверхности и объём будут 27320.508 и 235702.250 соответственно. Я воспользовался вашей программой чтобы их получить.
Хватит ли точности float чтобы представить объём с точностью до третьего знака после запятой?
Чтобы проверить выведем последовательные значения типа float:
#include <stdio.h>
#include <math.h>
int main() {
float x = 235702.250;
printf("%.10f\n%.10f\n%.10f\n", nextafterf(x, x - 1), x, nextafterf(x, x + 1));
}
$ gcc -std=c11 -pedantic -Wall -Wextra -Werror next.c -lm && ./a.out 235702.2343750000 235702.2500000000 235702.2656250000
Разница между соседними значениеми 0.015625. Больше одной тысячной. float просто не способен хранить такие числа с нужной точностью.
Заменим float на double:
#include <stdio.h>
#include <math.h>
int main() {
double p, d;
if (scanf("%lf %lf", &d, &p) != 2) {
return 1;
}
const double s = d * d + 2 * d * sqrt(p * p - (d * d) / 4);
const double h = sqrt(p * p - (d * d) / 2);
const double v = d * d * h / 3;
printf("%.3lf %.3lf\n", s, v);
}
$ gcc -std=c11 -pedantic -Wall -Wextra -Werror double.c -lm $ echo 100 100 | ./a.out 27320.508 235702.260
Объём отличается! Проверим точность:
#include <stdio.h>
#include <math.h>
int main() {
double x = 235702.260;
printf("%.20lf\n%.20lf\n%.20lf\n", nextafter(x, x - 1), x, nextafter(x, x + 1));
}
$ gcc -std=c11 -pedantic -Wall -Wextra -Werror next-double.c -lm && ./a.out 235702.25999999998020939529 235702.26000000000931322575 235702.26000000003841705620
Разница между последовательными значениями 2.9103830456733704e-11. Этого достаточно чтобы представить ответ точно.
Важно понимать что число выше не означает что ответ найден с точностью до 3*10^-11. Наоборот, это оценка снизу: ответ не может быть найден с точностью лучше чем 3*10^-11. Реальная точность программы всегда будет ниже. Насколько ниже - предмет для анализа, который выходит за рамки этого ответа. Можно надеяться что точности double хватит: она на семь порядков выше нужной.
Никогда не считайте во float без веских причин. У вас нет точного способа оценить точность расчётов. В такой ситуации лишних разрядов не бывает.
Зачем нужен float если им не пользоваться?
Чтобы считать большие матрицы из чисел, а памяти недостаточно. Нейронные сети попадают в этот пункт.
Чтобы считать на очень слабом железе без аппаратной поддержки вещественной арифметики. Например, на микроконтроллере вычисления на
floatбудут заметно быстрее чем наdouble.
Во всех остальных случаях только double.