如何在C中四舍五入浮点值(如37.777779)到小数点后两位(37.78)?
当前回答
没有办法将一个浮点数四舍五入为另一个浮点数,因为四舍五入的浮点数可能不可表示(浮点数的限制)。例如,假设你将37.777779四舍五入为37.78,但最接近的数字是37.781。
然而,你可以使用格式化字符串函数来“舍入”浮点数。
其他回答
此函数接受数字和精度,并返回四舍五入后的数字
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;
}
它通过左移浮点数并检查大于5的条件将浮点数转换为int。
使用float roundf(float x)。
舍入函数将其参数舍入为浮点格式中最接近的整数值,无论当前舍入方向如何,舍入距离为零的中间情况。C11dr§7.12.9.5
#include <math.h>
float y = roundf(x * 100.0f) / 100.0f;
根据你的浮点数实现,看起来是一半的数字并不是。因为浮点数通常是面向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,并四舍五入到最接近的浮动1.120000004768…
“1.125”是介于1.12和1.13之间的“中间值”,当转换为float时,值正好是1.125,是“中间值”。由于与偶数规则的关系,它四舍五入到1.13,并四舍五入到最接近的浮点数1.129999995232…
虽然“1.135”是介于1.13和1.14之间的“中间值”,但当转换为float时,其值为1.134999990463…并且不再是“半途”,而是更接近1.13,并舍入到最接近的浮动1.129999995232…
如果使用代码
y = roundf(x*100.0f)/100.0f;
虽然“1.135”是介于1.13和1.14之间的“中间值”,但当转换为float时,其值为1.134999990463…并且不再是“半路”,而是更接近1.13,但错误地舍入到浮动1.139999985695…由于浮点数和双精度数的精度更有限。这个不正确的值可能被视为正确的,这取决于编码目标。
代码定义:
#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
你仍然可以使用:
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
例子:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
为此,始终使用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,因此当四舍五入到小数点后两位时,数学上的正确答案是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不能精确地表示为double;最接近的值是863119242376661248.0。当你把它除以100,你得到8631192423766612.0 -一个不同于你开始的数字。
希望这足以说明使用roundf舍入到小数点后几位是错误的,应该使用snprintf。如果你觉得这是一个可怕的黑客,也许你会放心,因为这基本上就是CPython所做的。