在C中实现round()的简洁方法?

嵌入式C我使用的是没有round()函数它是数学库,在C中实现这个的简洁方法是什么? 我想将它打印成一个字符串,查找小数位,然后在句点后找到第一个字符,然后在> = 5时向上舍入,否则向下。 想知道是否有更聪明的东西。

谢谢,弗雷德

正如许多其他答案所暗示的那样,你可以重新发明轮子。 或者,您可以使用其他人的轮子 – 我建议使用Newlib,这是BSD许可的,旨在用于嵌入式系统。 它正确处理负数,NaN,无穷大和不能表示为整数的情况(由于太大),以及使用指数和屏蔽而不是通常成本较高的浮点运算的有效方式。 此外,它经常测试,所以你知道它没有明显的角落案例错误。

Newlib源可能有点难以导航,所以这里有你想要的位:

Float版本: https : //sourceware.org/git/gitweb.cgi? p = newlib-cygwin.git; a = blob; f = newlib / libm / common / sys_round.c; hb = master

双版本: https : //sourceware.org/git/gitweb.cgi?p = newlib-cygwin.git; a = blob; f = newlib / libm / common / s_round.c; hb = master

这里定义的字提取宏: https : //sourceware.org/git/gitweb.cgi?p = newlib-cygwin.git; a = blob; f = newlib / libm / common / fdlibm.h; hb = master

如果你需要其他文件,那么父目录是这样的: https : //sourceware.org/git/gitweb.cgi?p = newlib-cygwin.git; a = tree; f = newlib / libm / common; hb =主

为了记录,这里是float版本的代码。 如您所见,正确处理所有可能的情况需要一些复杂性。

 float roundf(x) { int signbit; __uint32_t w; /* Most significant word, least significant word. */ int exponent_less_127; GET_FLOAT_WORD(w, x); /* Extract sign bit. */ signbit = w & 0x80000000; /* Extract exponent field. */ exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127; if (exponent_less_127 < 23) { if (exponent_less_127 < 0) { w &= 0x80000000; if (exponent_less_127 == -1) /* Result is +1.0 or -1.0. */ w |= ((__uint32_t)127 << 23); } else { unsigned int exponent_mask = 0x007fffff >> exponent_less_127; if ((w & exponent_mask) == 0) /* x has an integral value. */ return x; w += 0x00400000 >> exponent_less_127; w &= ~exponent_mask; } } else { if (exponent_less_127 == 128) /* x is NaN or infinite. */ return x + x; else return x; } SET_FLOAT_WORD(x, w); return x; } 
 int round(double x) { if (x < 0.0) return (int)(x - 0.5); else return (int)(x + 0.5); } 
 int round(float x) { return (int)(x + 0.5); } 

警告:仅适用于正数。

IEEE 754建议采用“舍入一半到偶数”的方法:如果d的小数部分为0.5,则舍入到最接近的偶数整数。 问题是在相同方向上舍入0.5的小数部分会在结果中引入偏差; 因此,你必须将一小部分0.5上半部分时间向下舍入一半时间,因此“舍入到最接近的偶数整数”位,四舍五入到最接近的奇数也将起作用,因为将翻转一个公平的硬币来确定哪条路走。

我觉得更像这样的东西是IEEE纠正的:

 #include  int is_even(double d) { double int_part; modf(d / 2.0, &int_part); return 2.0 * int_part == d; } double round_ieee_754(double d) { double i = floor(d); d -= i; if(d < 0.5) return i; if(d > 0.5) return i + 1.0; if(is_even(i)) return i; return i + 1.0; } 

这个应该是C99-ish(似乎指出0.5的小数部分的数字应该从零开始舍入):

 #include  double round_c99(double x) { return (x >= 0.0) ? floor(x + 0.5) : ceil(x - 0.5); } 

而且我的第一个round_c99()一个更紧凑的版本,这个更好地处理56位尾数边界,不依赖于x+0.5x-0.5是明智的事情:

 #include  double round_c99(double d) { double int_part, frac_part; frac_part = modf(d, &int_part); if(fabs(frac_part) < 0.5) return int_part; return int_part > 0.0 ? int_part + 1.0 : int_part - 1.0; } 

如果|int_part| >> 1会出现问题 |int_part| >> 1但是用大指数舍入双精度是没有意义的。 我确信三者中都有NaN,但我的受虐狂有限制,数字编程确实不是我的事。

浮点计算有足够的空间容易出现细微错误,因此简洁可能不是最佳要求。

一个更好的解决方案是大致打击你的编译器供应商的面部和颈部,直到他们提供一个合适的数学库。

在古代没有很好地定义整个系统的舍入时,我们编写了一个缩放的舍入函数,该函数首先乘以数字,以便通过截断数字来完成舍入。
要舍入到2个小数位,乘以100,添加.5,截断结果并除以100。
这就是数控机床的工作方式,当控制装置无法运行NC程序时,除非是现场(死螺母)。

一种使用字符串操作的方法

 float var=1.2345; char tmp[12]={0x0}; sprintf(tmp, "%.2f", var); var=atof(tmp); 

使用数字运算的另一种方法

 float var=1.2345; int i=0; var*=100; i=var; var=i; var/=100; 

你能用整数吗? 请执行下列操作 :

 int roundup(int m, int n) { return (m + n - 1) / n ; } int rounddown(int m, int n) { return m / n; } int round(int m, int n) { int mod = m % n; if(mod >= (n + 1) / 2) return roundup(m, n); else return rounddown(m, n); }