Округление числа до 2 десятичных знаков в C

Как я могу округлить число с плавающей запятой (например, 37,777779) до двух знаков после запятой (37,78) в C?

 fbrereto27 авг. 2009 г., 23:49
Какие данные вы ожидаете? Еще один поплавок или печатный вывод?
 Pavel Minaev27 авг. 2009 г., 23:49
Вы не можете правильно округлить само число, потому чтоfloat (а такжеdoubleа)t десятичная с плавающей точкой - они являются двоичными числами с плавающей точкой - поэтому округление до десятичной позиции не имеет смысла. Вы можете округлить вывод, однако.
 Truthseeker Rangwan03 авг. 2014 г., 15:21
Что за округлениеожидаешь? Половина или округление до ближайшего четного?
 Brooks Moses28 авг. 2009 г., 05:08
Это'не бессмысленно; Это'неточно. Там'Это большая разница.

Ответы на вопрос(15)

ого ответа на этот вопрос. В идеальном мире округление не имеет большого значения. Однако в реальных системах вам, возможно, придется столкнуться с несколькими проблемами, которые могут привести к округлению, которое может не соответствовать вашим ожиданиям. Например, вы можете выполнять финансовые расчеты, где окончательные результаты округляются и отображаются для пользователей в виде 2 десятичных знаков; эти же значения хранятся с фиксированной точностью в базе данных, которая может содержать более 2 десятичных разрядов (по разным причинам; оптимального количества мест для хранения не существует ... зависит от конкретных ситуаций, которые должна поддерживать каждая система, например, крошечные предметы, цены на которые являются доли копейки на единицу); и вычисления с плавающей запятой, выполняемые для значений, где результаты плюс / минус эпсилон. Я сталкиваюсь с этими проблемами и разрабатываю свою собственную стратегию на протяжении многих лет. Я выиграл'Не утверждаю, что я сталкивался с каждым сценарием или имею лучший ответ, но ниже приведен пример моего подхода, который преодолевает эти проблемы:

Предположим, что 6 десятичных знаков считаются достаточной точностью для вычислений с плавающей запятой / двойным числом (произвольное решение для конкретного приложения) с использованием следующей функции / метода округления:

double Round(double x, int p)
{
    if (x != 0.0) {
        return ((floor((fabs(x)*pow(double(10.0),p))+0.5))/pow(double(10.0),p))*(x/fabs(x));
    } else {
        return 0.0;
    }
}

Округление до 2 знаков после запятой для представления результата может быть выполнено как:

double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));

Заval = 6.825результат6.83 как и ожидалось.

Заval = 6.824999результат6.82, Здесь предполагается, что расчет привел в точности6.824999 и 7-е десятичное место равно нулю.

Заval = 6.8249999результат6.83, 7-й знак после запятой9 в этом случае вызываетRound(val,6) функция, чтобы дать ожидаемый результат. В этом случае может быть любое количество трейлингов9s.

Заval = 6.824999499999результат6.83, В качестве первого шага округляем до 8-го знака после запятой, т.е.Round(val,8), заботится об одном неприятном случае, когда вычисленный результат с плавающей запятой вычисляется как6.8249995, но внутренне представлен как.6.824999499999...

Напоследок пример из вопроса ...val = 37.777779 результаты в.37.78

Этот подход может быть далее обобщен как:

double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));

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

Определение кода:
#define roundz(x,d) ((floor(((x)*pow(10,d))+.5))/pow(10,d))
Результаты :
a = 8.000000
sqrt(a) = r = 2.828427
roundz(r,2) = 2.830000
roundz(r,3) = 2.828000
roundz(r,5) = 2.828430

если выИспользуя C ++, вы можете просто создать такую функцию:

string prd(const double x, const int decDigits) {
    stringstream ss;
    ss << fixed;
    ss.precision(decDigits); // set # places after decimal
    ss << x;
    return ss.str();
}

Вы можете вывести любой двойнойmyDouble сn ставит после десятичной точки такой код:

std::cout << prd(myDouble,n);

Вы все еще можете использовать:

float ceilf(float x); // don't forget #include <math.h> and link with -lm.
</math.h>

пример:

float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
 ZeroCool28 авг. 2009 г., 10:02
В состояние сна :)
 Brooks Moses28 авг. 2009 г., 05:07
Однако округление до двух знаков после запятой является тривиальным вариантом (но все же следует упомянуть в ответе; ZeroCool, хотите добавить правку?): Float roundedValue = ceilf (valueToRound * 100.0) / 100.0;
 Andy03 дек. 2013 г., 06:35
Почему это решение нет более популярным? Это работает именно так, как это должно быть с минимальным кодом. Есть ли какое-то предостережение с этим?
 Pavel Minaev28 авг. 2009 г., 01:23
Это усекает в десятичной запятой (то есть будет производить 37), и ему нужно округлить до двух местпосле десятичная точка.
printf("%.2f", 37.777779);

Если вы хотите написать в C-строку:

char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
 Michael Haren28 авг. 2009 г., 16:20
@aib: Я думаю, потому что / ** / - это комментарии в стиле C, а вопрос помечен для C
 aib31 авг. 2009 г., 15:49
@Michael: Как и //
 aib28 авг. 2009 г., 16:17
@ Синан: Почему редактировать? @AraK: нет,вы следует позаботиться о размере :). Используйте snprintf ().
 Andrew Coleson04 сент. 2009 г., 19:28
C89 допускает только / ** / - стиль, C99 вводит поддержку // - стиля. Используйте неработающий / старый компилятор (или принудительно включите режим C89), и выЯ не смогу использовать // - стиль. Сказав это, это 'с 2009 годаРассмотрим их как стиль C и C ++.

float roundf(float x)

Функции округления округляют свой аргумент до ближайшего целочисленного значения в формате с плавающей запятой, округляя полпути до нуля, независимо от текущего направления округления. " C11dr §7.12.9.5

#include <math.h>
float y = roundf(x * 100.0f) / 100.0f; 
</math.h>

В зависимости от вашегоfloat реализации, цифры, которые могут показаться на полпути не являются. поскольку плавающая точка обычно ориентирована на базу-2. Далее точно округление до ближайшего0.01 на все "наполовину" случаи самые сложные.

void r100(const char *s) {
  float x, y;
  sscanf(s, "%f", &x);
  y = round(x*100.0)/100.0;
  printf("%6s %.12e %.12e\n", s, x, y);
}

int main(void) {
  r100("1.115");
  r100("1.125");
  r100("1.135");
  return 0;
}

 1.115 1.115000009537e+00 1.120000004768e+00  
 1.125 1.125000000000e+00 1.129999995232e+00
 1.135 1.134999990463e+00 1.139999985695e+00

Хотя "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...

Если код используется

y = roundf(x*100.0f)/100.0f;

Хотя "1,135" является "наполовину" между 1,13 и 1,14, при конвертации вfloat, значение1.134999990463... и больше ненаполовину", но ближе к 1.13 ноневерно раунды вfloat из1.139999985695... из-за более ограниченной точностиfloat противdouble, Это неправильное значение может рассматриваться как правильное, в зависимости от целей кодирования.

float другомуfloat потому что округлыеfloat может не быть представимым (ограничение чисел с плавающей точкой). Например, допустим, вы округлили с 37,777779 до 37,78, но ближайшим представимым числом является 37,781.

Тем не менее, выМожно "круглыйfloat с помощью функции форматирования строки.

 Brooks Moses28 авг. 2009 г., 05:11
Это ничем не отличается от высказываниятам'нет никакого способа разделить два числа и получить число, потому что разделенный результат не может быть представлен " что может быть точно верно, но не имеет значения. Поплавки всегда неточны, даже для чего-то такого базового, как сложение; всегда предполагается, что то, что вы на самом деле получаете "Поплавок, который наиболее близко соответствует точному округленному ответу ».
 Andrew Keeton28 авг. 2009 г., 05:28
Я имел в виду, что вы не можете обойтиfloat до n знаков после запятой, а затем ожидать, что результат всегда будет иметь n знаков после запятой. Вы все еще получитеfloatпросто не тот, который вы ожидали.
double f_round(double dval, int n)
{
    char l_fmtp[32], l_buf[64];
    char *p_str;
    sprintf (l_fmtp, "%%.%df", n);
    if (dval>=0)
            sprintf (l_buf, l_fmtp, dval);
    else
            sprintf (l_buf, l_fmtp, dval);
    return ((double)strtod(l_buf, &p_str));

}

n это число десятичных знаков

пример:

double d = 100.23456;

printf("%f", f_round(d, 4));// result: 100.2346

printf("%f", f_round(d, 2));// result: 100.23

это в свой заголовок / существо файла

#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))

Вот пример:

float x = ROUNDF(3.141592, 100)

х равен 3,14 :)

 Eric Postpischil01 дек. 2018 г., 22:07
Это усекает, но вопрос требует округления. Кроме того, он подвержен ошибкам округления в операциях с плавающей запятой.
 mou26 июн. 2016 г., 09:30
Отредактировано, верно

эта функция принимает число и точность и возвращает округленное число

float roundoff(float num,int precision)
{
      int temp=(int )(num*pow(10,precision));
      int num1=num*pow(10,precision+1);
      temp*=10;
      temp+=5;
      if(num1>=temp)
              num1+=10;
      num1/=10;
      num1*=10;
      num=num1/pow(10,precision+1);
      return num;
}

он преобразует число с плавающей точкой в int, сдвигая точку влево и проверяя условие больше пяти

"%.2f" Формат строки действительно правильный ответ. Однако, если вы действительно хотите округлить значение с плавающей запятой для дальнейших вычислений, работает что-то вроде следующего:

#include <math.h>

float val = 37.777779;

float rounded_down = floorf(val * 100) / 100;   /* Result: 37.77 */
float nearest = roundf(val * 100) / 100;  /* Result: 37.78 */
float rounded_up = ceilf(val * 100) / 100;      /* Result: 37.78 */
</math.h>

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

Как отметили некоторые другие, из-за причуд представления с плавающей запятой, эти округленные значения могут быть не совсем "очевидно» десятичные значения, но они будут очень и очень близки.

Для получения гораздо (намного!) Дополнительной информации о округлении, и особенно о правилах разрыва связи для округления до ближайшего, см.статья в Википедии о округлении.

 Dale Hagglund03 авг. 2014 г., 22:42
Вы'Совершенно верно, что это не подходит для отрицательных чисел. Я не былне в курсеroundf»Впрочем, так что спасибо.
 Ken23 янв. 2017 г., 23:45
какие'Разница между круглым и круглым?
 Ms. Nobody20 июн. 2014 г., 14:40
Могу ли я сделать эту работу сdoubles тоже как-то? Безразлично»кажется, я делаю то, что хочу :( (используяfloor а такжеceil).
 Apeiron29 янв. 2015 г., 00:11
будет ли какое-либо преимущество делатьroundf(val*100)/100 против?round(val / 1e-2) * 1e-2
 smileyborg16 мар. 2016 г., 04:30
Вот'Ваш общий макрос, который будет работать для произвольных десятичных знаков:#define ROUND_TO_DECIMAL_PLACES(x, decimal_places) (roundf(x * 1e##decimal_places) / 1e##decimal_places)
 user47230815 мая 2014 г., 17:09
@DaleHagglung Бывший, спасибо. Является ли корректировка для замены 100 на pow (10, (int) требуемая точность)?
 Dale Hagglund29 янв. 2015 г., 05:19
@Apeiron: я думаю, что ониБудет точно так же после того, как компилятор преобразует целочисленную константу 100 в значение с плавающей запятой. Однако я'Я утверждаю, что первое легче читать.
 Parag Bafna03 апр. 2017 г., 16:43
@ Ken плавать вокруг (плавать); двойной раунд (двойной);
 Dale Hagglund15 мая 2014 г., 09:09
@slater Когда ты говоришь:произвольная точностьВы спрашиваете о округлении, например, до трех вместо двух десятичных знаков, или используете библиотеки, которые реализуют десятичные значения с неограниченной точностью? Если первое, внесите, как я надеюсь, очевидные поправки к постоянной 100; в противном случае выполните точно такие же вычисления, как показано выше, с любой библиотекой с множественной точностью, которую вы используете.повторное использование.
 Dale Hagglund22 июн. 2014 г., 00:56
Это должно работать нормально, даже если все, что вы делаете, это изменитьпоплавок» кдвойной», Можете ли вы описать, какие у вас проблемы?
 user47230814 мая 2014 г., 22:12
Можно ли изменить его для поддержки округления до произвольной точности?
 Dale Hagglund16 мая 2014 г., 05:19
Ага. Для округления после k десятичных знаков используйте масштабный коэффициент 10 ^ k. Это должно быть действительно легко увидеть, если вы выписываете некоторые десятичные значения вручную и играете с кратными 10. Предположим, выВы работаете со значением 1.23456789 и хотите округлить его до 3 десятичных знаков. Доступная вам операцияокруглить до целого, Итак, как вы перемещаете первые три знака после запятой, чтобы онислева от десятичной точки? Я надеюсь на это'ясно, что вы умножаете на 10 ^ 3. Теперь вы можете округлить это значение до целого числа. Затем вы кладете обратно три младших разряда, разделив их на 10 ^ 3.

что выговорим о значении для печати, тоЭндрю Колсон а такжеарак»ответ правильный:

printf("%.2f", 37.777779);

Но учтите, что если выповторно округлить число до точно 37,78 для внутреннего использования (например, сравнить с другим значением), тогда это не 'Это хорошая идея, так как числа с плавающей запятой работают: вы обычно нене хочу сравнивать равенство для чисел с плавающей запятой, вместо этого используйте целевое значение +/- сигма-значение. Или закодируйте число в виде строки с известной точностью и сравните это.

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

 Anonymous White28 окт. 2012 г., 10:56
На самом деле 37.78 можно представить именно с плавающей запятой. Float имеет от 11 до 12 цифр для точности. Этого должно быть достаточно, чтобы адрес 3778 377,8 или все виды 4 десятичных цифр.
 John Carter08 нояб. 2012 г., 20:00
@HaryantoCiu да, честно, яя немного отредактировал мой ответ.
 Brooks Moses28 авг. 2009 г., 05:13
Голосовали за решение вопроса о том, что может быть за этим вопросом (или вопрос, который должен был стоять за этим вопросом!). Тот'Это довольно важный момент.

/* Function to control # of decimal places to be output for x */
double showDecimals(const double& x, const int& numDecimals) {
    int y=x;
    double z=x-y;
    double m=pow(10,numDecimals);
    double q=z*m;
    double r=round(q);

    return static_cast<double>(y)+(1.0/m)*r;
}
</double>

затемstd::cout << showDecimals(37.777779,2); будет производить: 37,78.

Очевидно, ты неМне действительно нужно создать все 5 переменных в этой функции, но я оставляю их там, чтобы вы могли видеть логику. Возможно, есть более простые решения, но это хорошо работает для меня - тем более что это позволяет мне корректировать количество цифр после десятичного знака по мере необходимости.

Как насчет этого:

float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
 Muhammad Adnan03 июн. 2015 г., 11:44
: D @Danil хороший ответ :)
 Shmil The Cat15 мая 2017 г., 17:43
правила округления в контексте комментария @Daniil являютсякруглый до ближайшего
 Daniil28 авг. 2009 г., 15:20
@therefromhere: (а) Вы 'прав (б) Что это? Тест средней школы?
 Daniil08 июн. 2016 г., 22:51
Это'Необходимо соблюдать правила округления.
 muhammad tayyab03 июн. 2016 г., 03:48
почему вы добавили 0,5?
 John Carter28 авг. 2009 г., 10:28
-1: а) это победилоt работает для отрицательных чисел (хорошо, пример положительный, но все же). б) ты нене говоря уже о том, что этоНевозможно сохранить точное десятичное значение в float
printf("%.2f", 37.777779);
 albert01 февр. 2014 г., 19:45
Этот способ лучше, потому что нет потери точности.
 chux03 авг. 2014 г., 17:52
@albert У этого также есть преимущество без потериfloat диапазон какval * 100 может переполниться.

Ваш ответ на вопрос