Как можно округлить значение с плавающей запятой (например, 37,777779) до двух десятичных знаков (37,78) в C?
c
floating-point
decimal
Сакиб Арифин
источник
источник
float
(иdouble
) не являются десятичными числами с плавающей точкой - они являются двоичными числами с плавающей точкой - поэтому округление до десятичных позиций не имеет смысла. Вы можете округлить вывод, однако.Ответы:
Если вы просто хотите округлить число для целей вывода, тогда
"%.2f"
строка формата действительно является правильным ответом. Однако, если вы действительно хотите округлить значение с плавающей запятой для дальнейших вычислений, работает что-то вроде следующего:Обратите внимание, что есть три различных правила округления, которые вы можете выбрать: округление в меньшую сторону (то есть усечение после двух десятичных знаков), округление до ближайшего и округление в большую сторону. Обычно вы хотите округлить до ближайшего.
Как отметили некоторые другие, из-за особенностей представления с плавающей запятой, эти округленные значения могут быть не совсем «очевидными» десятичными значениями, но они будут очень и очень близки.
Чтобы получить гораздо больше информации о округлении, и особенно о правилах разрыва связей для округления до ближайшего, см. Статью Википедии о округлении .
источник
doubles
слишком как-то? Кажется, не выполняет ту работу, которую я хочу :( (используюfloor
иceil
).Использование % .2f в printf. Он печатает только 2 десятичных знака.
Пример:
Вывод:
источник
float
дальности, посколькуval * 100
может переполниться.Предполагая, что вы говорите о значении для печати, ответ Эндрю Колесона и Арака верен:
Но учтите, что если вы хотите округлить число до 37,78 для внутреннего использования (например, для сравнения с другим значением), то это не очень хорошая идея из-за способа работы чисел с плавающей запятой: обычно вы этого не делаете хотите сделать сравнение равенства для плавающей запятой, вместо этого используйте целевое значение +/- сигма-значение. Или закодируйте число в виде строки с известной точностью и сравните это.
Смотрите ссылку в ответе Грега Хьюгилла на связанный вопрос , который также объясняет, почему вы не должны использовать плавающую точку для финансовых расчетов.
источник
printf("%.*f", (int)precision, (double)number);
Как насчет этого:
источник
Если вы хотите написать в C-строку:
источник
Нет способа округлить a
float
до другого,float
потому что округленноеfloat
может быть не представимым (ограничение чисел с плавающей запятой). Например, допустим, вы округлили 37,777779 до 37,78, но ближайшее представимое число - 37,781.Тем не менее, вы можете "округлить"
float
, используя функцию форматирования строки.источник
float
до n десятичных знаков, а затем ожидать, что результат всегда будет иметь n десятичных знаков. Вы все еще получитеfloat
, но не тот, который вы ожидали.Кроме того, если вы используете C ++, вы можете просто создать такую функцию:
Затем вы можете вывести любой двойник
myDouble
сn
местами после десятичной точки с помощью такого кода:источник
Вы все еще можете использовать:
пример:
источник
В C ++ (или в C с приведением в стиле C) вы можете создать функцию:
Тогда
std::cout << showDecimals(37.777779,2);
будет производить: 37,78.Очевидно, вам не нужно создавать все 5 переменных в этой функции, но я оставляю их там, чтобы вы могли видеть логику. Возможно, есть более простые решения, но это хорошо работает для меня - тем более, что это позволяет мне корректировать количество цифр после десятичного знака по мере необходимости.
источник
Всегда используйте
printf
семейство функций для этого. Даже если вы хотите получить значение как число с плавающей запятой, лучше всего использоватьsnprintf
для получения округленного значения в виде строки, а затем анализировать его с помощьюatof
:Я говорю это потому, что подход, показанный текущим ответом с наибольшим количеством голосов и несколькими другими здесь - умножение на 100, округление до ближайшего целого числа, а затем деление на 100 - ошибочно в двух отношениях:
Чтобы проиллюстрировать первый тип ошибки - иногда неправильное направление округления - попробуйте запустить эту программу:
Вы увидите этот вывод:
Обратите внимание, что значение, с которого мы начали, было меньше 0,015, и поэтому математически правильный ответ при округлении до 2 десятичных знаков равен 0,01. Конечно, 0,01 не совсем точно представляется в виде двойного числа, но мы ожидаем, что наш результат будет двойным, ближайшим к 0,01. Использование
snprintf
дает нам этот результат, но использованиеround(100 * x) / 100
дает нам 0,02, что неправильно. Зачем? Потому что100 * x
дает нам точно 1,5 в результате. Умножение на 100, таким образом, меняет правильное направление на округление.Для иллюстрации второго рода ошибки - результат иногда быть неправильно из - за
* 100
и на/ 100
самом деле не быть обратным друг друга - мы можем сделать подобное упражнение с очень большим числом:Наш номер теперь даже не имеет дробной части; это целочисленное значение, просто хранится с типом
double
. Таким образом, результат после округления должен быть таким же, как мы начали, верно?Если вы запустите программу выше, вы увидите:
К сожалению. Наш
snprintf
метод снова возвращает правильный результат, но подход «умножить-затем-округлить-затем-разделить» не дает результатов. Это потому , что математически правильное значение8631192423766613.0 * 100
,863119242376661300.0
является не совсем представимо в виде двойной; самое близкое значение863119242376661248.0
. Когда вы делите это обратно на 100, вы получаете8631192423766612.0
- число, отличное от того, с которого вы начали.Надеюсь, это достаточная демонстрация того, что использование
roundf
округления до числа десятичных разрядов не работает, и что вы должны использоватьsnprintf
вместо этого. Если вам кажется, что это ужасный хак, возможно, вы будете уверены, что это именно то, что делает CPython .источник
Использование
float roundf(float x)
.«Функции округления округляют свой аргумент до ближайшего целочисленного значения в формате с плавающей запятой, округляя полпути до нуля, независимо от текущего направления округления». C11dr §7.12.9.5
В зависимости от вашей
float
реализации, числа, которые могут показаться на полпути, не являются. поскольку с плавающей точкой, как правило, ориентируется на базу-2. Кроме того, точное округление до ближайшего0.01
во всех «промежуточных» случаях является наиболее сложной задачей.Хотя «1.115» является «полпути» между 1.11 и 1.12, при преобразовании в
float
значении не является1.115000009537...
и больше не « на полпути», но ближе к 1.12 и патронам к ближайшемуfloat
из1.120000004768...
«1.125» означает «на полпути» между 1.12 и 1.13, при преобразовании
float
в значение точно1.125
и «на полпути». Она округляет в стороне 1,13 из - за связи с даже правил и округляется до ближайшегоfloat
из1.129999995232...
Хотя «1.135» есть « на полпути» между 1.13 и 1.14, когда преобразуется в
float
значение не является1.134999990463...
и больше не « на полпути», но ближе к 1,13 и патронов к ближайшемуfloat
из1.129999995232...
Если код используется
Хотя «1.135» является «на полпути» между 1.13 и 1.14, при преобразовании
float
в значение равно1.134999990463...
и больше не «на полпути», а ближе к 1.13, но неправильно округляет доfloat
из-1.139999985695...
за более ограниченной точностиfloat
сравненияdouble
, Это неправильное значение может рассматриваться как правильное в зависимости от целей кодирования.источник
Я сделал этот макрос для округления чисел с плавающей точкой. Добавьте это в свой заголовок / существо файла
Вот пример:
х равен 3,14 :)
источник
Вот
n
количество десятичных знаковпример:
источник
dval
это огромный 3) сверхъестественныйif
/else
блок , где вы делаете одно и то же в каждой отрасли и 4) слишком сложное использованиеsprintf
для создания спецификатора формата для второгоsprintf
вызова; проще использовать.*
и передавать двойное значение и количество десятичных знаков в качестве аргументов для одного иsprintf
того же вызова.Определение кода:
Полученные результаты :
источник
Позвольте мне сначала попытаться обосновать причину добавления еще одного ответа на этот вопрос. В идеальном мире округление не имеет большого значения. Однако в реальных системах вам, возможно, придется столкнуться с несколькими проблемами, которые могут привести к округлению, которое может не соответствовать вашим ожиданиям. Например, вы можете выполнять финансовые расчеты, где окончательные результаты округляются и отображаются для пользователей в виде 2 десятичных знаков; эти же значения хранятся с фиксированной точностью в базе данных, которая может включать в себя более 2 десятичных знаков (по разным причинам; оптимального количества мест для хранения не существует ... зависит от конкретных ситуаций, которые должна поддерживать каждая система, например, крошечные предметы, цены на которые - доли копейки за единицу); и вычисления с плавающей запятой, выполняемые для значений, где результаты плюс / минус эпсилон. Я сталкиваюсь с этими проблемами и разрабатываю свою собственную стратегию на протяжении многих лет. Я не буду утверждать, что сталкивался с каждым сценарием или имею лучший ответ, но ниже приведен пример моего подхода, который преодолевает эти проблемы:
Предположим, что 6 знаков после запятой считаются достаточной точностью для вычислений с плавающей запятой / двойными числами (произвольное решение для конкретного приложения) с использованием следующей функции / метода округления:
Округление до 2 знаков после запятой для представления результата может быть выполнено как:
Для
val = 6.825
, результат,6.83
как и ожидалось.Ибо
val = 6.824999
результат есть6.82
. Здесь предполагается, что в результате расчета точно6.824999
и 7-е десятичное место равно нулю.Ибо
val = 6.8249999
результат есть6.83
. В9
этом случае седьмое десятичное место приводит к тому, чтоRound(val,6)
функция дает ожидаемый результат. В этом случае может быть любое количество конечных9
s.Ибо
val = 6.824999499999
результат есть6.83
. Округление до восьмого десятичного знака в качестве первого шага, т.Round(val,8)
Е. Учитывает один неприятный случай, когда вычисляемый результат с плавающей запятой вычисляется6.8249995
, но внутренне представляется как6.824999499999...
.Наконец, пример из вопроса ...
val = 37.777779
приводит к37.78
.Этот подход может быть далее обобщен как:
где N - точность, которая должна поддерживаться для всех промежуточных вычислений для чисел с плавающей запятой / двойников. Это работает и на отрицательных значениях. Я не знаю, является ли этот подход математически правильным для всех возможностей.
источник
Простой код C для округления числа:
Это будет вывод:
источник
... или вы можете сделать это старомодным способом без каких-либо библиотек:
Это, конечно, если вы хотите удалить дополнительную информацию из номера.
источник
эта функция принимает число и точность и возвращает округленное число
он преобразует число с плавающей запятой в int, сдвигая точку влево и проверяя условие больше пяти.
источник