Точность вычислений. Си
Мне нужно, чтобы по завершению программы число было выведено с точностью до 10^(-6) символов. Но возникла проблема. Число не точное.
#include #include int main()
После завершения этой программы выдает значение 30.000002 , хотя должно выдавать 30.000000 (a = 60, r = 10) Я попытался ограничить количество цифр после запятой до 5, но это не помогло.
Отслеживать
13.8k 12 12 золотых знаков 44 44 серебряных знака 77 77 бронзовых знаков
задан 25 сен 2023 в 19:39
user561670 user561670
printf(«%.5f» . См. en.cppreference.com/w/c/io/fprintf. А для расчетов используйте double .
25 сен 2023 в 19:40
А я что говорил о double ? ideone.com/2CeAW0
25 сен 2023 в 19:48
И, кстати, делите на 2. , не на 2 , а то введете нечетное значение и будете удивляться.
25 сен 2023 в 19:49
Странная идея вводить a и r целыми.
25 сен 2023 в 19:57
Вы хорошо понимаете что делает запись «%d» «%d» ?
25 сен 2023 в 19:58
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Семи разрядов точности float не хватает для вашей задачи: 30.000000 — восемь знаков. Используйте double — 16 разрядов.
#include #include // В стандартном C нет константы M_PI, определяем на всякий случай #ifndef M_PI # define M_PI (3.14159265358979323846) #endif int main() < // разумно ввести угол и радиус как вещественные числа // всегда все вещественные вычисления делайте на double // у вас не причин использовать float double a, r; // ввод всегда следует проверять if (scanf("%lf %lf", &a, &r) != 2) < fputs("error\n", stderr); return 1; >// синус нужен два раза, запомним его // все константы в вещественных выражениях с точкой // целочисленное деление свело с ума не одного программиста const double s = sin(a / 2. * (M_PI / 180.)); const double x = r * (1. + s) / (1. - s); // после печати переведите строку // печать вещественных чисел выводит шесть знаков после запятой, // ничего не надо менять printf("%lf\n", x); >
Компилировать лучше на максимуме предупреждений. Компилятор замечает такие огрехи в коде, которые программисту и в голову не придут:
$ gcc -std=c11 -pedantic -Wall -Wextra -Werror -Wwrite-strings -Wconversion temp.c -lm $ echo 60 10 | ./a.out 30.000000
Тип float
Числа с плавающей запятой используют формат IEEE (Института инженеров по электротехнике и электронике). Значения с одиночной точностью и типом float имеют 4 байта, состоят из бита знака, 8-разрядной двоичной экспоненты excess-127 и 23-битной мантиссы. Мантисса представляет число от 1,0 до 2,0. Поскольку бит высокого порядка мантиссы всегда равен 1, он не сохраняется в числе. Это представление обеспечивает для типа float диапазон примерно от 3,4E–38 до 3,4E+38.
Можно объявить переменные в качестве типа float или double в зависимости от нужд приложения. Основные различия между двумя типами значения заключаются в представляемой ими значимости, требуемых ресурсах хранения и диапазоне. В следующей таблице показана связь между значимостью и требованиями к хранению.
Типы с плавающей запятой
Type | Значимые цифры | Число байтов |
---|---|---|
float | 6–7 | 4 |
double | 15–16 | 8 |
Переменные с плавающей запятой представлены мантиссой, которая содержит значение числа, и экспонентой, которая содержит порядок возрастания числа.
В следующей таблице показано количество битов, выделенных мантиссе и экспоненте для каждого типа с плавающей запятой. Наиболее значимый бит любого типа float или double — всегда бит знака. Если он равен 1, число считается отрицательным; в противном случае — положительным.
Длина экспонент и мантисс
Type | Длина экспоненты | Длина мантиссы |
---|---|---|
float | 8 бит | 23 бита |
double | 11 бит | 52 бита |
Поскольку экспоненты хранятся в форме без знака, экспоненты смещены на половину своего возможного значения. Для типа float смещение составляет 127; для типа double это 1023. Можно вычислить фактическое значение экспоненты, вычтя значение смещения из значения экспоненты.
Мантисса хранится в виде бинарной доли, которая больше или равна 1 и меньше 2. Для типов float и double в мантиссе подразумевается наличие начального 1 в наиболее значимой битовой позиции, поэтому фактически длина мантисс составляет 24 и 53 бит соответственно, даже если наиболее значимый бит никогда не хранится в памяти.
Вместо только что описанного метода хранения пакет значений с плавающей запятой может хранить двоичные числа с плавающей запятой как денормализованные числа. Денормализованные числа — это ненулевые числа с плавающей запятой и зарезервированными значениями экспонент, в которых наиболее значимый бит мантиссы равен 0. Используя денормализованный формат, можно расширить диапазон числа с плавающей запятой в ущерб точности. Невозможно контролировать, в какой форме будет представлено число с плавающей запятой — нормализованной или денормализованной. Пакет значений с плавающей запятой определяет представление. В пакете значений с плавающей запятой никогда не используется денормализованная форма. Исключение составляют случаи, когда экспонента становится меньше, чем минимальное значение, которое может быть представлено в нормализованной форме.
В следующей таблице показаны минимальное и максимальное значения, которое можно сохранить в переменных каждого типа с плавающей запятой. Значения, указанные в этой таблице, применяются только к нормализованным числам с плавающей запятой; денормализованные числа с плавающей запятой имеют меньшее минимальное значение. Обратите внимание, что номера, сохраненные в регистрах 80x87, всегда представлены в 80-разрядной нормализованной форме; при сохранении в 32- или 64-разрядных переменных с плавающей запятой числа могут быть представлены только в ненормализованной форме (переменные типов float и long).
Диапазон типов с плавающей запятой
Type | Минимальное значение | Максимальное значение |
---|---|---|
плавающее | 1,175494351 E – 38 | 3,402823466 E + 38 |
double | 2,2250738585072014 E – 308 | 1,7976931348623158 E + 308 |
Если точность менее важна, чем размер хранимых данных, имеет смысл использовать тип float для переменных с плавающей запятой. И наоборот, если точность — наиболее важный критерий, используйте тип double.
Уровень переменных с плавающей запятой можно повысить до типа большей значимости (преобразование типа float в тип double). Повышение уровня часто происходит при выполнении арифметических действий с переменными плавающего типа. Это арифметическое действие всегда выполняется на том же уровне точности, что и переменная с наивысшим уровнем точности. Например, проанализируйте объявления следующих типов.
float f_short; double f_long; long double f_longer; f_short = f_short * f_long;
В предыдущем примере уровень переменной f_short повышается до типа double, а затем переменная умножается на f_long ; затем результат округляется до типа float и присваивается объекту f_short .
В следующем примере (с использованием объявлений из предыдущего примера) арифметическая операция выполняется на уровне точности переменной типа float (32-разрядной). Уровень результата затем повышается до уровня double.
f_longer = f_short * f_short;
c++: задать точность числа при выводе через printf
Подскажите, как задать точность вывода float / double числа через printf ? У меня сейчас вывод выглядит следующим образом:
printf("a: %20.16f, b: %20.16f, c: %20.16f", a, b, c);
и настроен под точность double , но иногда я использую float числа (для ускорения расчётов) и тогда мне нужна float точность без вывода дополнительных чисел Как корректнее реализовать выбор соответствующей точность? Есть варианты 1) определять тип данных и для каждого типа использовать свой вариант вывода
template void myprint()
2) формировать строку в зависимости от точности
const int prec = std::numeric_limits::digits10 + 1; const std::string format = "%" + std::to_string(4 + prec) + "." + std::to_string(prec); .
оба варианта с недостатками (во втором как-то приходится правильно сформировать строку $x.y , что требует относительно много кода для такой задачи, а в первом варианте слишком много if придется наставить) как можно оптимальнее разрешить эту проблему?
Точность при вычислении
Решаю олимпиадные задачи различного рода, когда работал в Паскале никогда и не задумывался над работой оператора»=» или «>=» в работе с вещественными числами, я думал, что с точностью сравнений и проблем не может быть. Но вот я перешел на C++ и один учитель мне сказал, что «==» или «> = < %.7lf", l); >int main() < freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); scanf("%lf\n", &a); scanf("%d", &n); solve(); return 0; >
Отслеживать
11.4k 5 5 золотых знаков 27 27 серебряных знаков 40 40 бронзовых знаков
задан 13 авг 2012 в 6:36
Евгений536 Евгений536
710 2 2 золотых знака 12 12 серебряных знаков 30 30 бронзовых знаков
не волнуйтесь, вы перешли на новый уровень сложности. скоро у вас будет level up 🙂
13 авг 2012 в 7:53
В Паскале правила игры точно такие же, т.к. все это рано или поздно упирается в IEEE754 (en.wikipedia.org/wiki/IEEE_floating_point)
26 янв 2016 в 7:47
5 ответов 5
Сортировка: Сброс на вариант по умолчанию
Проблема здесь не в «неточном» сравнении аппаратурой процессора, а в том, что некоторые десятичные дроби не могут быть точно преобразованы в двоичный формат чисел с плавающей точкой.
Еще одна проблема заключается в потере младших разрядов мантиссы при вычислениях в циклах.
Отслеживать
ответ дан 13 авг 2012 в 8:23
46.1k 6 6 золотых знаков 48 48 серебряных знаков 116 116 бронзовых знаков
Действительно ничего хорошего из сравнения типа == при операциях с вещественными числами не выйдет. Можно попробовать простой код:
double sum = 0; for ( int i = 0; i++; i
Гарантировать, что в итоге sum будет равным 1 в данном случаи невозможно. Может быть равен, а может и нет. Дело в машинной точности округления мат. операций. И ваш преподаватель все правильно говорит. Сравнение вида
fabs ( 1.0 - sum ) < 0.0000000000000001;
Даст верный результат всегда. ps Число 0.0000000000000001 взял просто так, но поверьте, такая ошибка вычислений очень мала, но все же точного результата ( sum == 1.0 ) вы не получите