我该如何在C语言中将浮点数(例如37.777779)四舍五入至小数点后两位(37.78)?
我该如何在C语言中将浮点数(例如37.777779)四舍五入至小数点后两位(37.78)?
如果您只是想为输出目的四舍五入数字,那么"%.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 */
请注意,您可能想要选择三种不同的舍入规则:向下取整(即在两个小数位后截断),四舍五入和向上取整。通常,您会选择四舍五入。
正如其他一些人指出的那样,由于浮点表示的怪癖,这些舍入值可能不是“明显”的十进制值,但它们非常接近。
有关舍入的更多(更多!)信息,特别是关于四舍五入到最近的打结规则,请参阅维基百科关于舍入的文章。
#define ROUND_TO_DECIMAL_PLACES(x, decimal_places) (roundf(x * 1e##decimal_places) / 1e##decimal_places)
- smileyborg使用printf的%.2f,它只会打印2个小数点。
示例:
printf("%.2f", 37.777779);
输出:
37.77
val*100
可能会溢出而导致浮点数范围减少的问题。 - chux - Reinstate Monica假设你是在谈论打印输出结果时需要四舍五入,那么Andrew Coleson和AraK的回答是正确的:
printf("%.2f", 37.777779);
但是请注意,如果您的目标是将数字精确舍入为37.78以进行内部使用(例如与另一个值进行比较),那么这不是一个好主意,因为浮点数的工作方式:通常不建议使用浮点数进行相等性比较,而是使用目标值+/- sigma 值。或者将数字编码为具有已知精度的字符串,并进行比较。
请参见Greg Hewgill在相关问题的回答中提供的链接,其中还涵盖了为什么不应该在财务计算中使用浮点数的原因。
printf("%.*f", (int)precision, (double)number);
- Minhas Kamal这样怎么样:
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);
在此情况下,始终使用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;
}
为了说明第一种错误-有时舍入方向错误-尝试运行此程序:
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
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);
}
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
。如果您觉得这是一个可怕的hack,也许您会放心知道这就是CPython基本上所做的。
snprintf
的结果可能取决于实现;dround(0.125, 2)
可能会得到 0.12
或 0.13
。https://godbolt.org/z/xz4K1P13z - tuedadtoa
,例如在 CPython 中的 _Py_dg_dtoa
。 - tueda此外,如果您正在使用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
舍入到另一个float
,因为舍入后的float
可能无法被表示。例如,假设您将37.777779舍入为37.78,但最接近的可表示数字是37.781。
然而,您可以通过使用格式化字符串函数来“舍入”float
。
float
四舍五入到n位小数,然后期望结果总是有n位小数。你仍然会得到一个float
,只不过不是你期望的那个。 - Andrew Keetonround()
函数:https://www.pythontutorial.net/advanced-python/python-rounding/ 令人惊讶的是,像这样基本的功能在C++中被省略了。 - Raleigh L.您仍然可以使用以下方法:
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
示例:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
/* 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 个变量,但我将它们保留在那里以便您可以看到逻辑。可能有更简单的解决方案,但对我来说这很有效--尤其是因为它允许我根据需要调整小数点后的位数。
float
(和double
)不是十进制浮点数,而是二进制浮点数,因此无法正确对数字本身四舍五入到小数位, 因此将它们四舍五入到小数位置没有意义。 但可以对输出进行四舍五入。 - Pavel Minaev