Как можно округлить значение с плавающей запятой (например, 37,777779) до двух десятичных знаков (37,78) в C?
Как можно округлить значение с плавающей запятой (например, 37,777779) до двух десятичных знаков (37,78) в C?
Ответы:
Если вы просто хотите округлить число для целей вывода, тогда "%.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 */
Обратите внимание, что есть три различных правила округления, которые вы можете выбрать: округление в меньшую сторону (то есть усечение после двух десятичных знаков), округление до ближайшего и округление в большую сторону. Обычно вы хотите округлить до ближайшего.
Как отметили некоторые другие, из-за особенностей представления с плавающей запятой, эти округленные значения могут быть не совсем «очевидными» десятичными значениями, но они будут очень и очень близки.
Чтобы получить гораздо больше информации о округлении, и особенно о правилах разрыва связей для округления до ближайшего, см. Статью Википедии о округлении .
doubles
слишком как-то? Кажется, не выполняет ту работу, которую я хочу :( (использую floor
и ceil
).
Использование % .2f в printf. Он печатает только 2 десятичных знака.
Пример:
printf("%.2f", 37.777779);
Вывод:
37.77
float
дальности, поскольку val * 100
может переполниться.
Предполагая, что вы говорите о значении для печати, ответ Эндрю Колесона и Арака верен:
printf("%.2f", 37.777779);
Но учтите, что если вы хотите округлить число до 37,78 для внутреннего использования (например, для сравнения с другим значением), то это не очень хорошая идея из-за способа работы чисел с плавающей запятой: обычно вы этого не делаете хотите сделать сравнение равенства для плавающей запятой, вместо этого используйте целевое значение +/- сигма-значение. Или закодируйте число в виде строки с известной точностью и сравните это.
Смотрите ссылку в ответе Грега Хьюгилла на связанный вопрос , который также объясняет, почему вы не должны использовать плавающую точку для финансовых расчетов.
printf("%.*f", (int)precision, (double)number);
Как насчет этого:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
printf("%.2f", 37.777779);
Если вы хотите написать в C-строку:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
Нет способа округлить a float
до другого, float
потому что округленное float
может быть не представимым (ограничение чисел с плавающей запятой). Например, допустим, вы округлили 37,777779 до 37,78, но ближайшее представимое число - 37,781.
Тем не менее, вы можете "округлить" float
, используя функцию форматирования строки.
float
до n десятичных знаков, а затем ожидать, что результат всегда будет иметь n десятичных знаков. Вы все еще получите float
, но не тот, который вы ожидали.
Кроме того, если вы используете 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.
пример:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
В C ++ (или в C с приведением в стиле C) вы можете создать функцию:
/* 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;
}
Тогда std::cout << showDecimals(37.777779,2);
будет производить: 37,78.
Очевидно, вам не нужно создавать все 5 переменных в этой функции, но я оставляю их там, чтобы вы могли видеть логику. Возможно, есть более простые решения, но это хорошо работает для меня - тем более, что это позволяет мне корректировать количество цифр после десятичного знака по мере необходимости.
Всегда используйте printf
семейство функций для этого. Даже если вы хотите получить значение как число с плавающей запятой, лучше всего использовать snprintf
для получения округленного значения в виде строки, а затем анализировать его с помощью atof
:
#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
double dround(double val, int dp) {
int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
char *buffer = malloc(charsNeeded);
snprintf(buffer, charsNeeded, "%.*f", dp, val);
double result = atof(buffer);
free(buffer);
return result;
}
Я говорю это потому, что подход, показанный текущим ответом с наибольшим количеством голосов и несколькими другими здесь - умножение на 100, округление до ближайшего целого числа, а затем деление на 100 - ошибочно в двух отношениях:
Чтобы проиллюстрировать первый тип ошибки - иногда неправильное направление округления - попробуйте запустить эту программу:
int main(void) {
// This number is EXACTLY representable as a double
double x = 0.01499999999999999944488848768742172978818416595458984375;
printf("x: %.50f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.50f\n", res1);
printf("Rounded with round, then divided: %.50f\n", res2);
}
Вы увидите этот вывод:
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
Обратите внимание, что значение, с которого мы начали, было меньше 0,015, и поэтому математически правильный ответ при округлении до 2 десятичных знаков равен 0,01. Конечно, 0,01 не совсем точно представляется в виде двойного числа, но мы ожидаем, что наш результат будет двойным, ближайшим к 0,01. Использование snprintf
дает нам этот результат, но использование round(100 * x) / 100
дает нам 0,02, что неправильно. Зачем? Потому что 100 * x
дает нам точно 1,5 в результате. Умножение на 100, таким образом, меняет правильное направление на округление.
Для иллюстрации второго рода ошибки - результат иногда быть неправильно из - за * 100
и на / 100
самом деле не быть обратным друг друга - мы можем сделать подобное упражнение с очень большим числом:
int main(void) {
double x = 8631192423766613.0;
printf("x: %.1f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.1f\n", res1);
printf("Rounded with round, then divided: %.1f\n", res2);
}
Наш номер теперь даже не имеет дробной части; это целочисленное значение, просто хранится с типом double
. Таким образом, результат после округления должен быть таким же, как мы начали, верно?
Если вы запустите программу выше, вы увидите:
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
К сожалению. Наш snprintf
метод снова возвращает правильный результат, но подход «умножить-затем-округлить-затем-разделить» не дает результатов. Это потому , что математически правильное значение 8631192423766613.0 * 100
, 863119242376661300.0
является не совсем представимо в виде двойной; самое близкое значение 863119242376661248.0
. Когда вы делите это обратно на 100, вы получаете 8631192423766612.0
- число, отличное от того, с которого вы начали.
Надеюсь, это достаточная демонстрация того, что использование roundf
округления до числа десятичных разрядов не работает, и что вы должны использовать snprintf
вместо этого. Если вам кажется, что это ужасный хак, возможно, вы будете уверены, что это именно то, что делает CPython .
Использование float roundf(float x)
.
«Функции округления округляют свой аргумент до ближайшего целочисленного значения в формате с плавающей запятой, округляя полпути до нуля, независимо от текущего направления округления». C11dr §7.12.9.5
#include <math.h>
float y = roundf(x * 100.0f) / 100.0f;
В зависимости от вашей 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
, Это неправильное значение может рассматриваться как правильное в зависимости от целей кодирования.
Я сделал этот макрос для округления чисел с плавающей точкой. Добавьте это в свой заголовок / существо файла
#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))
Вот пример:
float x = ROUNDF(3.141592, 100)
х равен 3,14 :)
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
dval
это огромный 3) сверхъестественный if
/ else
блок , где вы делаете одно и то же в каждой отрасли и 4) слишком сложное использование sprintf
для создания спецификатора формата для второго sprintf
вызова; проще использовать .*
и передавать двойное значение и количество десятичных знаков в качестве аргументов для одного и sprintf
того же вызова.
#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
Позвольте мне сначала попытаться обосновать причину добавления еще одного ответа на этот вопрос. В идеальном мире округление не имеет большого значения. Однако в реальных системах вам, возможно, придется столкнуться с несколькими проблемами, которые могут привести к округлению, которое может не соответствовать вашим ожиданиям. Например, вы можете выполнять финансовые расчеты, где окончательные результаты округляются и отображаются для пользователей в виде 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
. В 9
этом случае седьмое десятичное место приводит к тому, что Round(val,6)
функция дает ожидаемый результат. В этом случае может быть любое количество конечных 9
s.
Ибо val = 6.824999499999
результат есть 6.83
. Округление до восьмого десятичного знака в качестве первого шага, т. 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 - точность, которая должна поддерживаться для всех промежуточных вычислений для чисел с плавающей запятой / двойников. Это работает и на отрицательных значениях. Я не знаю, является ли этот подход математически правильным для всех возможностей.
Простой код C для округления числа:
float n = 3.56;
printf("%.f", n);
Это будет вывод:
4
... или вы можете сделать это старомодным способом без каких-либо библиотек:
float a = 37.777779;
int b = a; // b = 37
float c = a - b; // c = 0.777779
c *= 100; // c = 77.777863
int d = c; // d = 77;
a = b + d / (float)100; // a = 37.770000;
Это, конечно, если вы хотите удалить дополнительную информацию из номера.
эта функция принимает число и точность и возвращает округленное число
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, сдвигая точку влево и проверяя условие больше пяти.
float
(иdouble
) не являются десятичными числами с плавающей точкой - они являются двоичными числами с плавающей точкой - поэтому округление до десятичных позиций не имеет смысла. Вы можете округлить вывод, однако.