Как задать точность в c

от admin

Вывод double с нужной точностью и в нужном формате

Требуется вывести double с максимальной точностью, при этом целую часть, которая не превышает 999 вывести с пробелами на месте отсутствующих цифр, типа 9,12.

Вывожу следующим образом

Возникло 2 проблемы:

1) везде читал, что точность double определяется как std::numeric_limits<double>::digits10 , однако похоже, что можно вытащить больше значащих цифр, если задать точность в 20 цифр, то будут видны все 20 отличных от 0 цифр

2) не нашел, как можно задать размер целой части, чтобы заполнить отсутствующие цифры пробелами

Подскажите, что требуется сделать?

Zhihar's user avatar

Всё, чего вам не хватало, это задать заполнитель setfill и ширину setw выводимого поля:

αλεχολυτ's user avatar

Можно также написать класс, с функциональностью выводить как угодно. Например:

И пример программы:

Можно и манипулятор написать, но такой класс, думаю, более полезен в том смысле, что может выдавать целую и дробную части

AR Hovsepyan's user avatar

Во-первых, смысл величины digits10 для плавающего типа T заключается в том, что если вы возьмете десятичное строковое представление, преобразуете его в значение типа T , а затем преобразуете его из T обратно в десятичное строковое представление, то вы получите digits10 значащих цифр цифр, совпадающих с вашей исходной строкой. Например, если вы используете плавающий тип для хранения целых значений, то целые значения с таким количеством цифр будут представляться без потерь, как и соседние (+-1) целые значения.

Родственной величиной является величина max_digits10 , которая говорит, что если вы преобразуете плавающее значение типа T в десятичное строковое представление с сохранением max_digits10 старших значащих цифр, а затем преобразуете его обратно в тип T , то вы гарантированно получите исходное значение типа T .

Таким образом, величина digits10 описывает сохранение данных в преобразовании туда-обратно вида «строка10 -> плавающее -> строка10«. А величина max_digits10 описывает сохранение данных в преобразовании туда-обратно вида «плавающее -> строка10 -> плавающее».

Это, однако, совсем не означает, что десятичное строковое плавающее значение будет иметь только столько точных цифр. Огульно обзывать цифры за пределами этого количества «мусором» — очевидная профанация. Какие цифры являются точными, а какие нет — определяется спецификой ваших вычислений и известно только вам.

Очевидный пример: «традиционные» двоичные плавающие типы способны точно представлять степени двойки в пределах возможностей экспоненты. То есть std::pow(2, 512) даст вам точное значение типа double

в котором намного больше значащих цифр, чем digits10 или max_digits10 для double ( 15 и 17 соответственно). Никакого «мусора» в этом представлении нет (разумеется, если вы хотели вычислить именно 2 512 ) . Вас это не должно удивлять. Умение пользоваться такими возможностями плавающих представлений — это во многом и есть умение пользоваться плавающими типами в общем.

Во-вторых, что касается управления шириной поля при выводе — об этом вы уже получили ответы.

По ходу обсуждения оказалось, что имеет смысл остановиться подробнее на определении точности вещественного типа. Удивительно, но оказывается, что точность double совсем не жалкие 15 цифр, а намного, намного больше! И в подтверждение приводятся числа 2^(-100) и 2^512, которые действительно представляются точно. Так что же, получается что и в самом деле точность double больше, чем 15 цифр? К сожалению нет, такие заявления говорят лишь о непонимании вещественной арифметики.

Давайте посмотрим на представление чисел вещественным типом. Точность представления определяется его мантиссой, которая в случае числа двойной точности имеет длину 52 бита и соответственно может представить 2^52 значений. Теперь возьмем некое число, у которого в двоичном представлении биты мантиссы 53, 54, и т.д. являются нулями. Нетрудно видеть, что такое число будет представлено абсолютно точно. С бесконечной точностью! И таких чисел существует ровно 2^52.

Попробуем теперь немного уменьшить требования к точности. Возьмем, скажем, тысячу знаков после запятой — есть ли числа, которые представляются с такой точностью? Да, такие числа в самом деле есть, и из свойств множества действительных чисел видно, что их бесконечно много. Какую бы точность мы ни задали — сто, тысячу, миллион знаков после запятой, — оказывается, что существует бесконечно много чисел, представимых именно с такой точностью.

Получается, что точность вещественного типа это какая-то странная характеристика, которая произвольно меняется в зависимости от числа вплоть до бесконечности? Нет, именно в этом месте и происходит путаница! Точность представления отдельно взятого числа не имеет никакого отношения к точности вещественного типа. Слово одно, но под ним подразумеваются совершенно разные вещи. А собственно точность представления числа, хотя и кажется очень важной с бытовой точки зрения, в вещественной арифметике никакого значения не имеет. По той простой причине, что она непредсказуема — мы не можем определить точность представления по результату вычислений. Для этого надо этот результат сравнить с эталонным значением, а если мы и так его знаем, то зачем нам что-то вычислять?

Так что же такое точность вещественного типа? Представим, что у нас есть два числа, у которых первые 52 бита мантиссы совпадают, а различается бит 53. Эти два числа будут представлены одной и той же мантиссой, и соответственно, тем же самым числом двойной точности. Мы говорим, что точность double составляет 52 бита, и это означает простую вещь: double различает только числа, у которых отличаются первые 52 бита. Числа, у которых отличаются только биты 53 и последующие, представляются одной и той же мантиссой. Вот это и есть точность! Соответственно,в десятичном виде точность double составляет 15.45 десятичных цифр. Это означает, что числа, отличающиеся первыми 15 знаками всегда представляются разными значениями double . Числа с отличиями в 16-ом знаке могут представляться разными значениями (в 45% случаев), а могут и не отличаться (соответственно, в 55% случаев). Числа с отличием в 17-ом и более младших знаках имеют одинаковое представление в числе двойной точности.

Разумеется, тема точности вещественной арифметики немного сложнее этого простого определения. Для более серьезного ознакомления можно посоветовать Д. Кнут, том 2 «Получисленные алгоритмы«, глава 4.2.2 «Точность выполнения арифметических действий в системе с плавающей точкой«.

Читать:
Как увеличить расстояние между словами в html

Урок №33. Типы данных с плавающей точкой: float, double и long double

На этом уроке мы рассмотрим типы данных с плавающей точкой в языке С++, их точность и диапазон. Выясним, что такое экспоненциальная запись и как она используется, а также рассмотрим ошибки округления и дадим определения для nan и inf .

Типы данных с плавающей точкой

Целочисленные типы данных отлично подходят для работы с целыми числами, но есть ведь еще и дробные числа. И тут нам на помощь приходит тип данных с плавающей точкой (или «тип данных с плавающей запятой», англ. «floating point»). Переменная такого типа может хранить любые действительные дробные значения, например: 4320.0, -3.33 или 0.01226. Почему точка «плавающая»? Дело в том, что точка/запятая перемещается («плавает») между цифрами, разделяя целую и дробную части значения.

Есть три типа данных с плавающей точкой: float, double и long double. Язык C++ определяет только их минимальный размер (как и с целочисленными типами). Типы данных с плавающей точкой всегда являются signed (т.е. могут хранить как положительные, так и отрицательные числа).

Тип Минимальный размер Типичный размер
Тип данных с плавающей точкой float 4 байта 4 байта
double 8 байт 8 байт
long double 8 байт 8, 12 или 16 байт

Объявление переменных разных типов данных с плавающей точкой:

Если нужно использовать целое число с переменной типа с плавающей точкой, то тогда после этого числа нужно поставить разделительную точку и нуль. Это позволяет различать переменные целочисленных типов от переменных типов с плавающей запятой:

Обратите внимание, литералы типа с плавающей точкой по умолчанию относятся к типу double. f в конце числа означает тип float.

Экспоненциальная запись

Экспоненциальная запись очень полезна для написания длинных чисел в краткой форме. Числа в экспоненциальной записи имеют следующий вид: мантисса × 10 экспонент . Например, рассмотрим выражение 1.2 × 10 4 . Значение 1.2 — это мантисса (или «значащая часть числа»), а 4 — это экспонент (или «порядок числа»). Результатом этого выражения является значение 12000.

Обычно, в экспоненциальной записи, в целой части находится только одна цифра, все остальные пишутся после разделительной точки (в дробной части).

Рассмотрим массу Земли. В десятичной системе счисления она представлена как 5973600000000000000000000 кг . Согласитесь, очень большое число (даже слишком большое, чтобы поместиться в целочисленную переменную размером 8 байт). Это число даже трудно читать (там 19 или 20 нулей?). Но используя экспоненциальную запись, массу Земли можно представить, как 5.9736 × 10 24 кг (что гораздо легче воспринимается, согласитесь). Еще одним преимуществом экспоненциальной записи является сравнение двух очень больших или очень маленьких чисел — для этого достаточно просто сравнить их экспоненты.

В языке C++ буква е / Е означает, что число 10 нужно возвести в степень, которая следует за этой буквой. Например, 1.2 × 10 4 эквивалентно 1.2e4 , значение 5.9736 × 10 24 еще можно записать как 5.9736e24 .

Для чисел меньше единицы экспонент может быть отрицательным. Например, 5e-2 эквивалентно 5 * 10 -2 , что, в свою очередь, означает 5 / 10 2 или 0.05 . Масса электрона равна 9.1093822e-31 кг .

На практике экспоненциальная запись может использоваться в операциях присваивания следующим образом:

Почему в C++ для double в числе только 5 цифр после запятой

В языке C++ при определении переменных необходимо указать их тип, причём в дальнейшем тип менять нельзя.

Для числовых переменных распространёнными типами являются int, short, long, float и double.

К примеру, переменная с типом данных float — это число с плавающей точкой, для хранения которого используется 4 байта, а это 6-9 значимых цифр, обычно 7.

А переменная с типом данных double — это число с плавающей точкой, для хранения которого используется 8 байт, а это примерно 15-18 значимых цифр, обычно 16.

Зная это, рассмотрим следующий простейший код — для его выполнения, сохраните код в файл test.cpp:

Теперь скомпилируйте его:

Результат выполнения очень необычен:

Число 230.4732 обрезано до 230.473 — потерян одна цифра после запятой. А в числе типа double, значение которого 30949.12345678, обрезалось до 30949.1 — то есть вместо предполагаемых 15-18 значимых цифр получено только 6…

Отсюда возникает вопрос, почему в числе типа double можно использовать только 6 значимых цифр?

На самом деле, число double d содержит все цифры после запятой, которые присвоены этой переменной — всё дело в том, как выполняется проверка числа. У метода cout имеются свои настройки по умолчанию для вывода и форматирования чисел — если в числе больше 6 разрядов, то обрезаются цифры после запятой, если до запятой также больше 6 цифр, то используется научная запись числа, например 1.23457e+14.

Такое поведение по умолчанию cout можно исправить. Для этого, во-первых, необходимо добавить новый заголовок #include <iomanip>.

Во-вторых нужно использовать std::setprecision(n), где n — это новая величина точности.

При использовании в выражении out << setprecision(n) или in >> setprecision(n) устанавливает для параметра точности потока out или in значение ровное n.

Отредактирует код нашей программы:

Заново скомпилируем и выполним его. Результат:

Именно этот результат мы и ожидали:

Отметим ещё раз — в самой программе используются все возможные знаки и разряды чисел, цифры обрезались в момент вывода.

Ограничить поплавок двумя знаками после десятичной точки в C++

В этом посте будет обсуждаться, как ограничить значение с плавающей запятой двумя знаками после десятичной точки в C++.

1. Использование round() функция

Существует несколько вариантов ограничения числа с плавающей запятой двумя знаками после запятой, в зависимости от того, хотите ли вы округлить число до ближайшего, округлить в меньшую или большую сторону. Например, следующий код округляет число с плавающей запятой до двух знаков после запятой.

Похожие статьи