Как я могу объединить float (например, 37.777779) до двух знаков после запятой (37.78) в C?
Число округлений до 2 десятичных мест в C
Ответ 1
Если вы просто хотите округлить число для целей вывода, то строка формата "%.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 */
Обратите внимание, что вы можете выбрать три разных правила округления: округлить (т.е. усечь после двух знаков после запятой), округлить до ближайшего и округлить. Обычно вы хотите округлить до ближайшего.
Как отмечали некоторые другие, из-за особенностей представления с плавающей запятой эти округленные значения могут быть не совсем "очевидными" десятичными значениями, но они будут очень близки.
Для большей (много!) дополнительной информации о округлении, и особенно о правилах развязывания для округления до ближайшего, см. статью Википедии об округлении.
Ответ 2
Использование %.2f в printf. Он печатает только 2 десятичных знака.
Пример:
printf("%.2f", 37.777779);
Выход:
37.77
Ответ 3
Предполагая, что вы говорите о значении для печати, тогда Andrew Coleson и AraK ответят правильно:
printf("%.2f", 37.777779);
Но обратите внимание, что если вы хотите округлить число до 37.78 для внутреннего использования (например, для сравнения с другим значением), то это не очень хорошая идея из-за того, как работают числа с плавающей точкой: вы обычно не хотите выполнять сравнения равенств для с плавающей запятой, вместо этого используйте целевое значение +/- значение сигмы. Или кодируйте число как строку с известной точностью и сравните это.
См. ссылку в Greg Hewgill ответьте на соответствующий вопрос, в котором также рассказывается, почему вы не должны использовать с плавающей точкой для финансовых расчетов.
Ответ 4
Как насчет этого:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
Ответ 5
printf("%.2f", 37.777779);
Если вы хотите записать в C-строку:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
Ответ 6
Не существует способа округлить float
до другого float
, потому что округленный float
может быть не представимым (ограничение числа с плавающей запятой). Например, скажите, что вы округлены с 37.777779 по 37.78, но ближайший представимый номер равен 37.781.
Тем не менее, вы можете "закруглить" float
с помощью строковой функции формата.
Ответ 7
Кроме того, если вы используете С++, вы можете просто создать такую функцию:
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);
Ответ 8
Вы все еще можете использовать:
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
Пример:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
Ответ 9
В С++ (или в 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 переменных в этой функции, но я оставляю их там, чтобы вы могли видеть логику. Есть, вероятно, более простые решения, но это хорошо работает для меня, тем более что это позволяет мне корректировать количество цифр после десятичной точки по мере необходимости.
Ответ 10
Используйте 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
of 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
of 1.129999995232...
Если используется код
y = roundf(x*100.0f)/100.0f;
Хотя "1.135" - "на полпути" между 1.13 и 1.14, при преобразовании в float
значение 1.134999990463...
и больше не "на полпути" , а ближе к 1.13, а неправильно до float
из 1.139999985695...
из-за более ограниченной точности float
vs. double
. Это неправильное значение может считаться правильным, в зависимости от целей кодирования.
Ответ 11
Для этого всегда используйте семейство функций 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 - имеет два недостатка:
- Для некоторых значений он будет округляться в неправильном направлении, поскольку умножение на 100 изменяет десятичную цифру, определяющую направление округления, с 4 на 5 или наоборот, из-за неточности чисел с плавающей запятой
- Для некоторых значений умножение, а затем деление на 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.
Ответ 12
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
Ответ 13
Я сделал этот макрос для округления чисел с плавающей точкой. Добавьте это в свой заголовок/существо файла
#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))
Вот пример:
float x = ROUNDF(3.141592, 100)
х равен 3,14 :)
Ответ 14
Определение кода:
#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
Ответ 15
Попробуйте это. он даст двойной с двумя знаками после запятой.
int ttt = round(value * 100);
string str = to_string(ttt);
str.insert (str.end()-2,1,'.');
double temp = ::strtod(str.c_str(), 0);
obPrinciple = temp;
Ответ 16
Позвольте мне сначала попытаться оправдать мою причину добавления еще одного ответа на этот вопрос. В идеальном мире округление на самом деле не имеет большого значения. Однако в реальных системах вам может потребоваться несколько проблем, которые могут привести к округлению, что может и не быть тем, что вы ожидаете. Например, вы можете выполнять финансовые расчеты, когда конечные результаты округляются и отображаются пользователям как 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
, а седьмое десятичное число - нулю.
Для val = 6.8249999
результат 6.83
. 7-е число после запятой 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 - точность, которая должна поддерживаться для всех промежуточных вычислений по поплавкам/удвоениям. Это работает и с отрицательными значениями. Я не знаю, является ли этот подход математически правильным для всех возможностей.
Ответ 17
... или вы можете сделать это старомодным способом без каких-либо библиотек:
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;
Это, конечно, если вы хотите удалить дополнительную информацию из номера.
Ответ 18
эта функция принимает число и точность и возвращает округленное число
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 по левому сдвигу точки и проверяет более пяти условий.