将数字舍入到C中的2个小数位

时间:2009-08-27 21:41:58

标签: c floating-point decimal

如何将浮点数(例如37.777779)舍入到C中的两位小数(37.78)?

18 个答案:

答案 0 :(得分:377)

如果您只想将数字四舍五入以进行输出,那么"%.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 */

请注意,您可能要选择三种不同的舍入规则:向下舍入(即,在两位小数后截断),四舍五入到最接近,并向上舍入。通常,你想要舍入到最近的。

正如其他几个人所指出的,由于浮点表示的怪癖,这些舍入值可能不是“明显的”十进制值,但它们将非常接近。

关于四舍五入的更多(更多!)信息,尤其是关于舍入到最近的打破平局规则,请参阅the Wikipedia article on Rounding

答案 1 :(得分:67)

printf("%.2f", 37.777779);

答案 2 :(得分:39)

假设您正在讨论打印值,那么Andrew ColesonAraK的回答是正确的:

printf("%.2f", 37.777779);

但请注意,如果你的目标是将数字四舍五入到内部使用的37.78(例如与另一个值进行比较),那么这不是一个好主意,因为浮点数的工作方式:你通常不希望对浮点进行相等比较,而是使用目标值+/- sigma值。或者将数字编码为具有已知精度的字符串,并进行比较。

请参阅Greg Hewgill's answer to a related question中的链接,其中还列出了您不应使用浮点进行财务计算的原因。

答案 3 :(得分:23)

这个怎么样:

float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);

答案 4 :(得分:19)

printf("%.2f", 37.777779);

如果你想写C-string:

char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);

答案 5 :(得分:11)

没有办法将float舍入到另一个float,因为舍入的float可能无法表示(浮点数的限制)。例如,假设您将37.777779舍入到37.78,但最接近的可表示数字是37.781。

但是,可以使用格式字符串函数“舍入”float

答案 6 :(得分:8)

另外,如果你使用的是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);

答案 7 :(得分:7)

您仍然可以使用:

float ceilf(float x); // don't forget #include <math.h> and link with -lm.

示例:

float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;

答案 8 :(得分:6)

在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个变量,但我将它们留在那里以便您可以看到逻辑。可能有更简单的解决方案,但这对我很有用 - 特别是因为它允许我根据需要调整小数位后的位数。

答案 9 :(得分: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;
}

之所以这样说,是因为currently top-voted answer和其他几种方法所显示的方法-  乘以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不能精确地表示为double,但是我们希望我们的结果是最接近0.01的double。使用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。如果您觉得这是骇人听闻的骇客,也许您会以basically what CPython does为前提而放心。

答案 10 :(得分:4)

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

答案 11 :(得分:4)

使用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并且是“中途”。由于与偶数规则的联系并向最近float 1.129999995232...的{​​{1}}进行轮次,它向1.13方向转向

虽然“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但是由于float1.139999985695...的精确度较为有限,错误会轮到float double。根据编码目标,可能会将此错误值视为正确。

答案 12 :(得分:3)

我用这个宏来舍入浮点数。 将其添加到标题/文件

#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))

以下是一个例子:

float x = ROUNDF(3.141592, 100)

x等于3.14:)

答案 13 :(得分:2)

代码定义:

#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

答案 14 :(得分:0)

让我首先尝试证明我为这个问题添加另一个答案的理由。在一个理想的世界里,四舍五入并不是什么大问题。但是,在实际系统中,您可能需要应对可能导致舍入的几个问题,这可能与您的预期不符。例如,您可能正在执行财务计算,其中最终结果被舍入并以2位小数显示给用户;这些相同的值以固定的精度存储在一个数据库中,该数据库可能包含2个以上的小数位(由于各种原因;没有最佳的位置数量......取决于每个系统必须支持的特定情况,例如价格很小的物品是每单位一分钱的分数);并且,对结果为正/负epsilon的值执行浮点计算。多年来,我一直在面对这些问题并制定自己的战略。我不会声称我已经面对每一个场景或得到了最好的答案,但下面是我迄今为止克服这些问题的方法的一个例子:

假设使用以下舍入函数/方法,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。在这种情况下,第7个小数位为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是浮点数/双精度数上所有中间计算的精度。这也适用于负值。我不知道这种方法在数学上是否适用于所有可能性。

答案 15 :(得分:0)

...或者您可以在没有任何库的情况下以老式的方式进行操作:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    return tableView ==  tableViewQR ?  qRinstructionList.count :  instructionList.count
}

如果要从号码中删除多余的信息,当然可以。

答案 16 :(得分:0)

用于舍入数字的简单C代码:

float n = 3.56;
printf("%.f", n);

这将输出:

4

答案 17 :(得分:-1)

此函数获取数字和精度并返回舍入的数字

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。