如何在C中将浮点值(例如37.777779)四舍五入到小数点后两位(37.78)?
如何在C中将浮点值(例如37.777779)四舍五入到小数点后两位(37.78)?
Answers:
如果您只是想舍入数字以便输出,那么"%.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 */
请注意,您可能要选择三种不同的舍入规则:向下舍入(即,在两个小数位后截断),四舍五入到最接近和四舍五入。通常,您想四舍五入到最接近的位置。
正如其他几个人指出的那样,由于浮点数表示的怪异,这些四舍五入的值可能不完全是“显而易见的”十进制值,但它们将非常接近。
有关舍入的更多(更多!)更多信息,尤其是关于四舍五入到最接近的平局决胜规则的更多信息,请参见Wikipedia上有关舍入的文章。
doubles
某种方式使这项工作吗?似乎并没有做的工作我想:((使用floor
和ceil
)。
在printf中使用%.2f。它只打印2个小数点。
例:
printf("%.2f", 37.777779);
输出:
37.77
float
范围的优点,因为val * 100
可能会溢出。
假设您正在谈论打印价值,那么Andrew Coleson和AraK的答案是正确的:
printf("%.2f", 37.777779);
但是请注意,如果您打算将该数字四舍五入到37.78以供内部使用(例如,与另一个值进行比较),那么由于浮点数的工作方式,这不是一个好主意:您通常不这样做想要对浮点进行相等比较,而是使用目标值+/-一个sigma值。或将数字编码为具有已知精度的字符串,然后进行比较。
请参阅格雷格·休吉尔(Greg Hewgill)对相关问题的解答中的链接,该链接还涵盖了为什么不应该使用浮点数进行财务计算。
printf("%.*f", (int)precision, (double)number);
printf("%.2f", 37.777779);
如果要写入C字符串:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
没有办法一轮float
到另一个float
,因为圆形float
可能无法表示(浮点数的限制)。例如,假设您将37.777779舍入为37.78,但是最接近的可表示数字是37.781。
但是,您可以float
通过使用格式字符串函数来 “取整” a 。
float
到n个小数位,然后期望结果始终具有n个小数位。您仍然会得到float
,但不是您期望的。
您仍然可以使用:
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,因此将其四舍五入到小数点后两位时的数学正确答案为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);
}
现在,我们的数字甚至不占分数。它是一个整数值,仅与type存储在一起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)
x等于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
调用会更简单。
让我首先尝试证明为这个问题添加另一个答案的理由。在理想的世界中,舍入并不是什么大问题。但是,在实际系统中,您可能需要应对几个问题,这些问题可能导致舍入可能不是您期望的。例如,您可能正在执行财务计算,将最终结果四舍五入并以小数点后两位显示给用户;这些相同的值以固定的精度存储在一个数据库中,该数据库可能包含两个以上的小数位(由于各种原因;没有最佳的保留位数...取决于每个系统必须支持的特定情况,例如价格很小的商品是每单位一分钱的分数);对结果为正负ε的值执行浮点计算。这些年来,我一直在面对这些问题并发展自己的策略。我不会声称自己已遇到所有情况或给出最佳答案,但以下是到目前为止克服这些问题的一种方法示例:
假定使用以下舍入函数/方法,小数点后六位被认为足以计算浮点数/双精度数(对于特定应用是任意决定):
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;
}
}
四舍五入到小数点后两位以表示结果可按以下方式执行:
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
。9
在这种情况下,小数点后第七位使Round(val,6)
函数给出预期的结果。对于这种情况,可能有任意数量的尾随9
。
对于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是浮点数/双精度数的所有中间计算要维持的精度。这也适用于负值。我不知道这种方法在数学上是否对所有可能性都正确。
四舍五入的简单C代码:
float n = 3.56;
printf("%.f", n);
这将输出:
4
float
(和double
)不是十进制浮点数-它们是二进制浮点数-因此舍入到十进制位置是没有意义的。但是,您可以舍入输出。