6

通常,四舍五入到小数点后两位很容易

printf("%.2lf",<variable>);

但是,舍入系统通常会四舍五入到最接近的偶数。例如,

2.554 -> 2.55
2.555 -> 2.56
2.565 -> 2.56
2.566 -> 2.57

而我想要实现的是

2.555 -> 2.56
2.565 -> 2.57

事实上,四舍五入在 C 中是可行的,但仅适用于整数;

int a = (int)(b+0.5)

因此,我要求如何在正值上使用 2 个小数位而不是 Integer 来执行与上述相同的操作,以实现我之前所说的 print

4

3 回答 3

2

目前尚不清楚您是否真的要“四舍五入”,或者更确切地说“从零舍入一半”,这需要对负值进行不同的处理。

单精度二进制float至少精确到小数点后 6 位,并且 20 位double,因此通过 DBL_EPSILON(在 float.h 中定义)微调 FP 值将导致n.nn5printf( "%.2lf", x )值向上舍入到下一个第 100 位。不影响非n.nn5值的显示值

double x2  = x * (1 + DBL_EPSILON) ; // round half-away from zero
printf( "%.2lf", x2 ) ;

对于不同的舍入行为:

double x2  = x * (1 - DBL_EPSILON) ;  // round half-toward zero
double x2  = x + DBL_EPSILON ;        // round half-up
double x2  = x - DBL_EPSILON ;        // round half-down
于 2014-08-03T19:37:43.460 回答
1

double以下是将 a 舍入到最接近的精确代码0.01 double

除了它处理之外的代码函数x = round(100.0*x)/100.0;使用操作来确保精确地完成 100.0 的缩放而没有精度损失。

这可能比 OP 感兴趣的代码更多,但它确实有效。

它适用于整个double范围。(仍然应该做更多的单元测试)。 这取决于,这是常见的。 -DBL_MAXDBL_MAX
FLT_RADIX == 2

#include <float.h>
#include <math.h>

void r100_best(const char *s) {
  double x;
  sscanf(s, "%lf", &x);

  // Break x into whole number and fractional parts.  
  // Code only needs to round the fractional part.  
  // This preserves the entire `double` range.
  double xi, xf;
  xf = modf(x, &xi);

  // Multiply the fractional part by N (256). 
  // Break into whole and fractional parts.
  // This provides the needed extended precision.
  // N should be >= 100 and a power of 2.
  // The multiplication by a power of 2 will not introduce any rounding.
  double xfi, xff;
  xff = modf(xf * 256, &xfi);

  // Multiply both parts by 100.  
  // *100 incurs 7 more bits of precision of which the preceding code
  //   insures the 8 LSbit of xfi, xff are zero.
  int xfi100, xff100;
  xfi100 = (int) (xfi * 100.0);
  xff100 = (int) (xff * 100.0); // Cast here will truncate (towards 0)

  // sum the 2 parts.  
  // sum is the exact truncate-toward-0 version of xf*256*100
  int sum = xfi100 + xff100;
  // add in half N
  if (sum < 0)
    sum -= 128;
  else
    sum += 128;

  xf = sum / 256;
  xf /= 100;
  double y = xi + xf;
  printf("%6s %25.22f ", "x", x);
  printf("%6s %25.22f %.2f\n", "y", y, y);
}

int main(void) {
  r100_best("1.105");
  r100_best("1.115");
  r100_best("1.125");
  r100_best("1.135");
  r100_best("1.145");
  r100_best("1.155");
  r100_best("1.165");
  return 0;
}
于 2014-08-03T23:51:27.380 回答
1

[编辑] OP 澄清只有打印的值需要四舍五入到小数点后 2 位。

OP 观察到,每“舍入到偶数”或“从零舍入”的数字“中途”舍入是误导性的。在 0.005、0.015、0.025、... 0.995 等 100 个“中途”数字中,通常只有 4 个恰好是“中途”:0.125、0.375、0.625、0.875。这是因为浮点数格式使用 base-2 并且无法精确表示 2.565 之类的数字。

取而代之的是,像这样的样本数是假设binary642.565的最接近的double值 。将该数字四舍五入到最接近的 0.01 应该是 2.56 而不是 OP 所希望的 2.57。2.564999999999999947...

因此,只有以 0.125 和 0.625 结尾的数字恰好位于中间区域并向下舍入,而不是 OP 所希望的向上舍入。建议接受并使用:

printf("%.2lf",variable);  // This should be sufficient

为了接近 OP 的目标,可以 A)针对以 0.125 或 0.625 结尾的数字进行测试,或者 B)略微增加。最小的增加将是

#include <math.h>
printf("%.2f", nextafter(x, 2*x));

使用@Clifford 可以找到另一种轻推方法。


double[将 a 舍入到最接近double的 0.01 倍数的前答案]

典型的浮点使用像binary64这样的格式,它采用 base-2。“四舍五入到最接近的数学 0.01 并远离 0.0”具有挑战性。

正如@Pascal Cuoq 提到的那样,浮点数2.555通常只是接近2.555并且具有更精确的值,例如不是2.555000000000000159872...一半。

下面的@BLUEPIXY 解决方案是最好的和实用的。

x = round(100.0*x)/100.0;

“舍入函数将其参数舍入为浮点格式的最接近的整数值,从零开始舍入一半情况,而不管当前舍入方向如何。” C11dr §7.12.9.6。

((int)(100 * (x + 0.005)) / 100.0)方法有两个问题:对于负数(OP 未指定),它可能会朝错误的方向舍入,并且整数通常具有更小的范围(INT_MININT_MAXdouble


仍然有一些情况,当double x = atof("1.115");它最终接近 1.12 时,它真的应该是1.11因为1.115, as adouble真的更接近1.11而不是“中途”。

string   x                         rounded x 
1.115 1.1149999999999999911182e+00 1.1200000000000001065814e+00

OP 没有指定负数的舍入,假设y = -f(-x).

于 2014-08-03T18:22:40.607 回答