C语言 在 C 中实现 round() 的简洁方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4572556/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Concise way to implement round() in C?
提问by fred basset
The embedded C I'm using doesn't have a round() function it it's math lib, what would be a concise way to implement this in C? I was thinking of printing it to a string, looking for the decimal place, then finding the first char after the period, then rounding up if >= 5, else down. etc. Was wondering if there's something more clever.
我使用的嵌入式 C 没有 round() 函数,它是数学库,在 C 中实现它的简洁方法是什么?我想把它打印成一个字符串,寻找小数位,然后找到句点后的第一个字符,然后如果 >= 5 则四舍五入,否则向下舍入。等想知道是否有更聪明的东西。
Thanks, Fred
谢谢,弗雷德
回答by Brooks Moses
You could re-invent the wheel, as many other answers suggest. Alternately, you could use someone else's wheel -- I'd suggest Newlib's, which is BSD-licensed and intended for use on embedded systems. It properly handles negative numbers, NaNs, infinities, and cases which are not representable as integers (due to being too large), as well as doing so in an efficient manner that uses exponents and masking rather than generally-costlier floating-point operations. In addition, it's regularly tested, so you know it doesn't have glaring corner-case bugs in it.
正如许多其他答案所建议的那样,您可以重新发明轮子。或者,您可以使用其他人的轮子——我建议使用 Newlib 的轮子,它是 BSD 许可的,旨在用于嵌入式系统。它正确处理负数、NaN、无穷大和不能表示为整数的情况(由于太大),并且以使用指数和掩码而不是通常成本更高的浮点运算的有效方式进行处理。此外,它会定期进行测试,因此您知道它没有明显的边角错误。
The Newlib source can be a bit awkward to navigate, so here are the bits you want:
Newlib 源代码可能有点难以导航,因此这里是您想要的部分:
Float version: 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/sf_round.c;hb =master
Double version: 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/s_round.c;hb=master
Word-extraction macros defined here: 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 =blob;f=newlib/libm/common/fdlibm.h;hb =master
If you need other files from there, the parent directory is this one: https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=tree;f=newlib/libm/common;hb=master
如果你需要其他文件,父目录是这个:https: //sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=tree;f=newlib/libm/common;hb =主人
For the record, here's the code for the float version. As you can see, there's a bit of complexity required to deal with all the possible cases correctly.
为了记录,这里是浮动版本的代码。如您所见,正确处理所有可能的情况需要一些复杂性。
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;
}
回答by dan04
int round(double x)
{
if (x < 0.0)
return (int)(x - 0.5);
else
return (int)(x + 0.5);
}
回答by nmichaels
int round(float x)
{
return (int)(x + 0.5);
}
Caveat:Only works on positive numbers.
警告:仅适用于正数。
回答by mu is too short
IEEE 754 recommends the "round half to even" approach: if the fractional part of dis 0.5 then round to the nearest even integer. The problem is that rounding a fractional part of 0.5 the same direction introduces bias in the results; so, you have to round a fractional 0.5 up half the time and down half the time, hence the "round to the nearest even integer" bit, rounding to the nearest odd would also work as would flipping a fair coin to determine which way to go.
IEEE 754 推荐“四舍五入”方法:如果 的小数部分d是 0.5,则四舍五入到最接近的偶数。问题是将小数部分 0.5 向同一方向四舍五入会导致结果出现偏差;因此,您必须将小数 0.5 向上舍入一半,向下舍入一半,因此“舍入到最接近的偶数整数”位,舍入到最接近的奇数也可以像抛硬币一样确定哪种方式走。
I think something more like this would be IEEE-correct:
我认为更像这样的事情将是 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;
}
And this one should be C99-ish (which appears to specify that numbers with fractional parts of 0.5 should be rounded away from zero):
这个应该是 C99-ish(它似乎指定小数部分为 0.5 的数字应该从零四舍五入):
#include <math.h>
double round_c99(double x) {
return (x >= 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
}
And a more compact version of my first round_c99(), this one handles crossing the 56bit mantissa boundary better by not relying on x+0.5or x-0.5being sensible things to do:
和我的第一个更紧凑的版本round_c99(),这个版本通过不依赖x+0.5或x-0.5不做明智的事情来更好地跨越 56 位尾数边界:
#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;
}
This will have problems if |int_part| >> 1but rounding a double with a large exponent is pointless. I'm sure there are NaN in all three as well but my masochism has limits and numerical programming really isn't my thing.
如果|int_part| >> 1四舍五入一个大指数的 double 是没有意义的,这将有问题。我确定这三个中也有 NaN,但我的受虐狂是有限制的,数值编程真的不是我的事。
Floating point computation has ample room for subtle errors so concisemay not be the best requirement.
浮点计算有足够的空间来处理细微的错误,所以简洁可能不是最好的要求。
An even better solution would be to beat your compiler vendor roughly about the face and neck until they provide a proper math library.
更好的解决方案是粗略地击败您的编译器供应商,直到他们提供适当的数学库。
回答by Dave
In the ancient days when rounding was not well defined across systems, we wrote a scaled rounding function that first multiplied the number so that the rounding was done by truncating the number.
To round to 2 decimal places, multiply by 100, add .5, truncate the results and divide by 100.
This is how it was done for Numerical Control machine tools when the controls couldn't run a NC program unless it was spot on (dead nuts).
在过去没有很好地跨系统定义舍入的时代,我们编写了一个缩放舍入函数,首先将数字相乘,以便通过截断数字来完成舍入。
要四舍五入到小数点后 2 位,乘以 100,加上 0.5,截断结果并除以 100。
当数控机床无法运行数控程序时,它就是这样做的,除非它在 (死坚果)。
回答by jim mcnamara
One way using a string operation
使用字符串操作的一种方式
float var=1.2345;
char tmp[12]={0x0};
sprintf(tmp, "%.2f", var);
var=atof(tmp);
Another way using numeric operations
使用数值运算的另一种方式
float var=1.2345;
int i=0;
var*=100;
i=var;
var=i;
var/=100;
回答by Hao
Can you use integer ? do the following :
你能用整数吗?请执行下列操作 :
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);
}
回答by HamsHammer
Here is my interpretation of the solution to rounding a double to an integer. This, of course, is not the standard for C but provides the functionality of rounding a double to the nearest integer.
这是我对将双精度舍入为整数的解决方案的解释。当然,这不是 C 的标准,但提供了将双精度舍入到最接近的整数的功能。
int round(double n){
int trunc = (int) n;
double diff = n - (double) trunc;
if(diff < 0.5){
return trunc;
} else {
return trunc+1;
}
}

