Concise way to implement round() in C?
我正在使用的嵌入式C没有Math函数库,它没有round()函数,用C来实现的简洁方法是什么? 我正在考虑将其打印为字符串,寻找小数位,然后在句点之后找到第一个字符,如果> = 5,则四舍五入,否则向下舍入。 等。想知道是否还有更聪明的东西。
谢谢,
弗雷德
正如许多其他答案所暗示的那样,您可以重新发明轮子。或者,您可以使用其他人的轮子-我建议您使用Newlib,它是BSD许可的,旨在用于嵌入式系统。它可以正确处理负数,NaN,无穷大和不能用整数表示的情况(由于太大),并且可以有效地使用指数和掩码,而不是使用通常比较昂贵的浮点运算。此外,它已经过定期测试,因此您知道它没有明显的极端错误。
Newlib的源代码浏览起来可能有些笨拙,所以这里是您想要的代码:
浮动版本:
https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/sf_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=master
作为记录,这是float版本的代码。如您所见,正确处理所有可能的情况需要一些复杂性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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; } |
1 2 3 4 5 6 7 | int round(double x) { if (x < 0.0) return (int)(x - 0.5); else return (int)(x + 0.5); } |
1 2 3 4 | int round(float x) { return (int)(x + 0.5); } |
注意:仅适用于正数。
IEEE 754建议使用"舍入到偶数"方法:如果
我认为更像这样的东西是IEEE正确的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
并且该值应为C99格式(它似乎指定小数部分为0.5的数字应四舍五入为零):
1 2 3 4 |
并且是我的第一个
1 2 3 4 5 6 7 8 |
如果
浮点计算有足够的空间处理细微的错误,因此简洁可能不是最佳要求。
更好的解决方案是在编译器供应商提供合适的数学库之前,先将他们的面孔和脖子打败。
在远古的系统中,舍入没有很好地定义,我们编写了一个缩放的舍入函数,该函数首先将数字相乘,以便通过截断数字来进行舍入。
要四舍五入到小数点后两位,请乘以100,再加上.5,截断结果再除以100。
当控件无法运行NC程序(除非发现)(死螺母)时,这就是数控机床的操作方式。
轻松实现
1 2 3 4 5 6 | /* Round to nearest, ties away from zero */ float my_roundf (float a) { const float rndofs = 0.499999970f; // 0x1.fffffep-2 return TRUNCF (a + COPYSIGNF (rndofs, a)); } |
其中
以目前的计算机速度,不可能详尽地测试双精度
1 2 3 4 5 6 | /* Round to nearest, ties away from zero */ double my_round (double a) { const double rndofs = 0.49999999999999994; // 0x1.fffffffffffffp-2 return trunc (a + copysign (rndofs, a)); } |
这是完整的C测试应用程序,用于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <math.h> #define BUILTIN_TRUNC (1) // truncf() is available #define USE_FLOOR_ONLY (0) // how to construct truncf() if not available #define BUILTIN_COPYSIGN (1) // copysignf() is available #if BUILTIN_TRUNC #define TRUNCF(a) truncf(a) #else // BUILTIN_TRUNC #define TRUNCF(a) my_truncf(a) #endif // BUILTIN_TRUNC #if BUILTIN_COPYSIGN #define COPYSIGNF(a,b) copysignf((a),(b)) #else // BUILTIN_COPYSIGN #define COPYSIGNF(a,b) copysignf_pos((a),(b)) #endif // BUILTIN_COPYSIGN /* re-interpret the bit pattern of a float (IEEE-754 binary32) as a uint32_t */ float uint32_as_float (uint32_t a) { float r; memcpy (&r, &a, sizeof r); return r; } /* re-interpret the bit pattern of a uint32_t as a float (IEEE-754 binary32) */ uint32_t float_as_uint32 (float a) { uint32_t r; memcpy (&r, &a, sizeof r); return r; } /* Forces the sign of b onto non-negative a */ float copysignf_pos (float a, float b) { uint32_t ia = float_as_uint32 (a); uint32_t ib = float_as_uint32 (b); return uint32_as_float (ia | (ib & 0x80000000)); } float my_truncf (float a) { #if USE_FLOOR_ONLY return COPYSIGNF (floorf (fabsf (a)), a); #else // USE_FLOOR_ONLY return (a < 0.0f) ? ceilf (a) : floorf (a); #endif // USE_FLOOR_ONLY } /* Round to nearest, ties away from zero */ float my_roundf (float a) { const float rndofs = 0.499999970f; // 0x1.fffffep-2 return TRUNCF (a + COPYSIGNF (rndofs, a)); } /* Round to nearest, ties away from zero */ double my_round (double a) { const double rndofs = 0.49999999999999994; // 0x1.fffffffffffffp-2 return trunc (a + copysign (rndofs, a)); } int main (void) { uint32_t argi, resi, refi; float arg, res, ref; #if BUILTIN_TRUNC printf ("Testing roundf() implemented via truncf()\ "); #else // BUILTIN_TRUNC #if USE_FLOOR_ONLY printf ("Testing roundf() implemented via floorf()\ "); #else // USE_FLOOR_ONLY printf ("Testing roundf() implemented via floorf() and ceilf()\ "); #endif // USE_FLOOR_ONLY #endif // BUILTIN_TRUNC argi = 0; do { arg = uint32_as_float (argi); res = my_roundf (arg); ref = roundf (arg); /* compare bit pattern for identity */ resi = float_as_uint32 (res); refi = float_as_uint32 (ref); if (resi != refi) { printf ("FAILED @ %08x (% 15.8e): res = %08x (% 15.8e) res = %08x (% 15.8e)\ ", argi, arg, resi, res, refi, ref); return EXIT_FAILURE; } argi++; } while (argi); printf ("PASSED\ "); return EXIT_SUCCESS; } |
我的实现是:
1 2 3 4 5 6 7 8 9 | int round(double x) { double diff = +x - (int) +x; if (x == 0) return 0; if (x < 0) { return (int) (+diff >= 0.5) ? x + (1 - diff) : x + (-1 - diff); } else { return (int) (diff >= 0.5) ? x + (1 - diff) : x - diff; } } |
然后
1 2 3 4 5 6 7 8 9 10 11 12 13 |
我希望这会有用^^
这是我对将双精度数舍入为整数的解决方案的解释。当然,这不是C的标准,但是提供了将double舍入到最接近的整数的功能。
1 2 3 4 5 6 7 8 9 | int round(double n){ int trunc = (int) n; double diff = n - (double) trunc; if(diff < 0.5){ return trunc; } else { return trunc+1; } } |
可以使用整数吗?请执行下列操作 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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); } |
一种使用字符串操作的方式
使用数字运算的另一种方式
1 2 3 4 5 6 | float var=1.2345; int i=0; var*=100; i=var; var=i; var/=100; |