我正在使用的嵌入式C没有round()函数它是数学库,在C中实现它的简洁方法是什么?我想将它打印成一个字符串,查找小数位,然后在句点之后找到第一个字符,然后如果> = 5则向上舍入,否则向下。想知道是否有更聪明的东西。
谢谢, 佛瑞德
答案 0 :(得分:17)
你可以重新发明轮子,正如许多其他答案所暗示的那样。或者,您可以使用别人的方向盘 - 我建议使用Newlib,这是BSD许可的,旨在用于嵌入式系统。它正确处理负数,NaN,无穷大和不能表示为整数的情况(由于太大),以及使用指数和屏蔽而不是通常成本较高的浮点运算的有效方式。此外,它经常测试,所以你知道它没有明显的角落错误。
Newlib源可能有点难以导航,所以这里有你想要的位:
如果你需要其他文件,那么父目录是这样的: https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=tree;f=newlib/libm/common;hb=master
记录中,这是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;
}
答案 1 :(得分:13)
int round(double x)
{
if (x < 0.0)
return (int)(x - 0.5);
else
return (int)(x + 0.5);
}
答案 2 :(得分:7)
int round(float x)
{
return (int)(x + 0.5);
}
警告:仅适用于正数。
答案 3 :(得分:7)
IEEE 754建议采用“舍入一半到均匀”的方法:如果d
的小数部分为0.5,则舍入到最接近的偶数整数。问题是在相同方向上舍入0.5的小数部分会在结果中引入偏差;所以,你必须将一小部分0.5上半部分时间向下舍入一半时间,因此“舍入到最接近的偶数整数”位,四舍五入到最接近的奇数也会起作用,因为会翻转一个公平的硬币以确定哪条路去。
我认为更像这样的事情将是IEEE纠正的:
#include <math.h>
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 <math.h>
double round_c99(double x) {
return (x >= 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
}
我的第一个round_c99()
的更紧凑版本,这个版本更好地处理56位尾数边界,而不依赖于x+0.5
或x-0.5
是明智的事情:
#include <math.h>
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
会出现问题,但使用大指数舍入双精度是没有意义的。我确信三者中都有NaN,但我的受虐狂有限制,数字编程确实不是我的事。
浮点计算有足够的空间容易出现细微错误,因此简明可能不是最佳要求。
一个更好的解决方案是在你的编译器供应商大致面对面之前,直到他们提供一个合适的数学库。
答案 4 :(得分:2)
在古代整个系统中没有明确定义舍入时,我们编写了一个缩放舍入函数,该函数首先乘以数字,以便通过截断数字来完成舍入。
要舍入到2个小数位,乘以100,加上.5,截断结果并除以100
这就是数控机床的工作方式,当控制装置无法运行NC程序时,除非它是现场(死螺母)。
答案 5 :(得分:0)
使用字符串操作的一种方法
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;
答案 6 :(得分:0)
你能用整数吗?执行以下操作:
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);
}
答案 7 :(得分:0)
这是我对将双精度数舍入为整数的解决方案的解释。当然,这不是C的标准,而是提供了将double舍入到最接近的整数的功能。
int round(double n){
int trunc = (int) n;
double diff = n - (double) trunc;
if(diff < 0.5){
return trunc;
} else {
return trunc+1;
}
}
答案 8 :(得分:0)
我的实现是:
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;
}
}
然后
#include <stdio.h>
int round(double x);
int main() {
printf("%d", round(0.6)); // returns 1
printf("%d", round(-0.6)); // returns 0
printf("%d", round(0.4)); // returns 0
printf("%d", round(-0.4)); // returns -1
return 0;
}
我希望这会有用^^
答案 9 :(得分:0)
round()
四舍五入到最接近的整数,并且四舍五入关系从零舍入。硬件通常不提供这种舍入模式。可以通过trunc()
轻松地模拟它。如果trunc()
也不可用,则可以依次通过单独的floor()
或结合使用floor()
和ceil()
进行仿真。哪种方法最有效取决于硬件功能以及这些标准C库函数如何映射到硬件指令。
对于float
来说,很容易就可以穷尽地对各种可能性进行原型设计和测试,而将自己生成的roundf()
简洁地实现为:
/* 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));
}
其中TRUNCF
和COPYSIGNF
是解析为内置函数或如上所述仿真的宏。有关详细信息,请参见下面的自包含测试应用程序。
以目前的计算机速度,不可能详尽地测试双精度round()
,但是由于类似的构造,人们可以确信正确的操作:
/* 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测试应用程序,用于对roundf()
的设计替代方案进行详尽的测试。假定float
映射到IEEE-754 binary32
类型。
#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()\n");
#else // BUILTIN_TRUNC
#if USE_FLOOR_ONLY
printf ("Testing roundf() implemented via floorf()\n");
#else // USE_FLOOR_ONLY
printf ("Testing roundf() implemented via floorf() and ceilf()\n");
#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)\n",
argi, arg, resi, res, refi, ref);
return EXIT_FAILURE;
}
argi++;
} while (argi);
printf ("PASSED\n");
return EXIT_SUCCESS;
}