Вывод double с нужной точностью и в нужном формате
Требуется вывести double с максимальной точностью, при этом целую часть, которая не превышает 999 вывести с пробелами на месте отсутствующих цифр, типа 9,12.
Вывожу следующим образом
Возникло 2 проблемы:
1) везде читал, что точность double определяется как std::numeric_limits<double>::digits10 , однако похоже, что можно вытащить больше значащих цифр, если задать точность в 20 цифр, то будут видны все 20 отличных от 0 цифр
2) не нашел, как можно задать размер целой части, чтобы заполнить отсутствующие цифры пробелами
Подскажите, что требуется сделать?
![]()
Всё, чего вам не хватало, это задать заполнитель setfill и ширину setw выводимого поля:
![]()
Можно также написать класс, с функциональностью выводить как угодно. Например:
И пример программы:
Можно и манипулятор написать, но такой класс, думаю, более полезен в том смысле, что может выдавать целую и дробную части
![]()
Во-первых, смысл величины 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 «Точность выполнения арифметических действий в системе с плавающей точкой«.
Урок №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() функция
Существует несколько вариантов ограничения числа с плавающей запятой двумя знаками после запятой, в зависимости от того, хотите ли вы округлить число до ближайшего, округлить в меньшую или большую сторону. Например, следующий код округляет число с плавающей запятой до двух знаков после запятой.