fre*_*set 14 c math floating-point
我使用的嵌入式C没有round()函数它是数学库,在C中实现它的简洁方法是什么?我想把它打印成一个字符串,查找小数位,然后在句点后找到第一个字符,然后在> = 5时向上舍入,否则向下.想知道是否有更聪明的东西.
谢谢,弗雷德
Bro*_*ses 18
正如许多其他答案所暗示的那样,你可以重新发明轮子.或者,您可以使用别人的方向盘 - 我建议使用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 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;
}
Run Code Online (Sandbox Code Playgroud)
dan*_*n04 13
int round(double x)
{
if (x < 0.0)
return (int)(x - 0.5);
else
return (int)(x + 0.5);
}
Run Code Online (Sandbox Code Playgroud)
int round(float x)
{
return (int)(x + 0.5);
}
Run Code Online (Sandbox Code Playgroud)
警告:仅适用于正数.
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;
}
Run Code Online (Sandbox Code Playgroud)
而这个应该是C99-ish(它似乎指明0.5的小数部分的数字应该从零开始舍入):
#include <math.h>
double round_c99(double x) {
return (x >= 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
}
Run Code Online (Sandbox Code Playgroud)
而我的第一个更紧凑的版本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;
}
Run Code Online (Sandbox Code Playgroud)
如果|int_part| >> 1用大指数舍入双精度是没有意义的话,这会有问题.我确信三者中都有NaN,但我的受虐狂有限制,数字编程确实不是我的事.
浮点计算有足够的空间容易出现细微错误,因此简洁可能不是最佳要求.
一个更好的解决方案是大致打击你的编译器供应商的面部和颈部,直到他们提供一个合适的数学库.