将浮点舍入到C中最接近的整数
我可能有一个noob问题,但搜索网站并没有发现任何问题。我正在学习用C语言编程,我试图从头开始构建一个函数,将浮点值舍入到最接近的整数,而不使用math.h。这是我的密码:将浮点舍入到C中最接近的整数,c,C,我可能有一个noob问题,但搜索网站并没有发现任何问题。我正在学习用C语言编程,我试图从头开始构建一个函数,将浮点值舍入到最接近的整数,而不使用math.h。这是我的密码: void main() { float b; for(b = 0; b <= 2; b = b + 0.1) { printf("%f ", b); printf("%i ", (int)b); printf("%f ", b -
void main()
{
float b;
for(b = 0; b <= 2; b = b + 0.1)
{
printf("%f ", b);
printf("%i ", (int)b);
printf("%f ", b - (int)b);
printf("Nearest: ");
if((b - (int)b)<0.5)
printf("%i ", (int)b);
else
printf("%i ", (int)b + 1);
printf("Function: %i ", round_near(b));
printf("\n");
}
getchar();
}
int round_near(float b)
{
if((b - (int)b)<0.5)
return(int)b;
else
return (int)b + 1;
}
void main()
{
浮球b;
对于(b=0;b当我试图在gcc下编译时,我得到了以下错误:
/tmp/x1.c:23: error: conflicting types for ‘round_near’
/tmp/x1.c:23: note: an argument type that has a default promotion can’t match an empty parameter name list declaration
/tmp/x1.c:16: error: previous implicit declaration of ‘round_near’ was here
您得到的有趣结果是,您的编译器在第一次遇到round\u near
时不知道它的定义,并假设它是int round\u near()
。因此这导致了未定义的行为
如果您将round_移动到main上方
附近,或者在main上方放入声明,您应该会得到预期的结果。您没有int round_附近(float b)
的原型,因此您依赖于隐式声明
尝试将此添加到代码中
int round_near (float b); // Prototype
int main(void) // Nitpick: main returns an int!
使用(b)
附近的round_的隐式声明,b
被提升为double。但定义假定它是一个浮点,具有不同的二进制布局,因此会得到疯狂的随机结果
你应该确保你的代码在编译时没有任何警告,以避免这种情况。隐式声明在语言中的唯一原因是向后兼容,但过去十年或二十年中的每一个编译器都会警告你编译不好。像-241…而不是1或2这样的输出通常表示未初始化的整数。。。
但是,只有在将int main()
函数移到int main()
之前,或者在int main()
之前简单地插入该函数的空白定义(如int round\u near(float b);
)之后,您的代码才能在Linux上使用GNU C编译器(gcc)进行编译,这就是“原型设计”
否则,函数将被“视”为int round\u near()
(请参见缺少参数定义),因此程序将打印出未初始化的整数
另一方面,这样的实践不会产生可移植的代码,因此如果不进行下面的修改,您的(实际上是C)代码可能会在Visual Studio中编译……但不会使用其他编译器
另一个离题:不要在循环中使用浮点数。浮点数很讨厌!很好地回答了OP的直接问题:int round\u near(…)
的隐含函数签名与int round\u near(float b)
和round\u near(b)的调用不兼容
以double
的形式传递b
简单的解决方案:原型化功能
关于round\u near()
强制转换为int
会严重缩小合法范围。最好使用long
带有负数的一般错误功能。代码应测试符号
下面是一个利用long-long
范围的解决方案,因为它通常大于float
可以精确表示的整数的连续范围。或者OP可以用round\u-near()
替换my\u-round()
大约40%的时间失败
#include <limits.h>
#include <stdio.h>
float my_roundf(float x) {
// Large `float`s typically have no fractional portion to round
if (x > LLONG_MAX / 2) return x;
if (x < LLONG_MIN / 2) return x;
return x > 0 ? (long long) (x + 0.5f) : (long long) (x - 0.5f);
}
float rand_float(void) {
union {
unsigned char uc[sizeof(float)];
float f;
} u;
do {
unsigned i;
for (i = 0; i < sizeof(float); i++) {
u.uc[i] = rand();
}
} while (u.f != u.f); // re-do if NaN encountered
return u.f;
}
void my_roundf_test(void) {
unsigned n = 100000;
while (n-- > 0) {
float x = rand_float();
float ymath = roundf(x);
// float ymy = round_near(x);
float ymy = my_roundf(x);
// Exact half-way cases may fail
if (ymath != ymy) {
printf("x:% .9e math:% .9e my:% .9e\n", x, ymath, ymy);
}
}
}
#包括
#包括
浮动我的圆圈F(浮动x){
//大的“float”通常没有整数部分
如果(x>LLONG_MAX/2)返回x;
如果(x0?(长-长)(x+0.5f):(长-长)(x-0.5f);
}
浮动随机浮动(无效){
联合{
无符号字符uc[sizeof(float)];
浮动f;
}u;
做{
未签名的i;
对于(i=0;i0){
浮动x=随机浮动();
浮点数ymath=roundf(x);
//浮动ymy=靠近(x)的圆形_;
浮动ymy=我的整轮f(x);
//确切的中途案例可能会失败
如果(ymath!=ymy){
printf(“x:%.9e数学:%.9e我的:%.9e\n”,x,ymath,ymy);
}
}
}
注意:每一个浮点舍入模式、负零点等都有确切的中途情况,要考虑一个完整的答案。但是把它留到另一天。 < P>一个简单的代码(<代码>浮点< /代码>将不适用于<代码> int /代码>类型,所以<代码>长隆< /代码>
长圆形(浮动a){
长b=a;
如果(a>=0)
返回a-b<0.5?b:b+1;
其他的
返回b-a<0.5?b:b-1;
}
工作:不要忘记负片…@EugeneSh。在什么情况下(int)b
会大于b
?void main()
-->int main(void)
我认为这是一个很棒的问题。如果我要教别人C语言中这一晦涩难懂的部分,我最好用这个问题作为例子。请问为什么是未定义的行为?如果在int main()之前没有正确定义的原型,你的函数将被称为int round\u near()
而不是int-round\u near(1.2)
。看到区别了吗?b被丢弃,因此参数b没有值=>参数b没有值(int)b,依此类推……这“您的函数将被称为int-round\u near()
而不是int-round\u near(1.2)
”不正确。C是不同的。问题不是缺少原型,而是float
被提升为double
(参见下面问题C的答案)。如果OP将编码为四舍五入(double f)
即使没有原型,代码也可以正常工作。它仍然是唯一完整的答案。畏缩我没有想到检查我的原型是否包含在内。典型的新手错误。尽管如此,它很有趣,我没有意识到浮点数和双精度点数不容易互换。我认为其中一个只是另一个的较短版本,如int还有一个很长的内部问题,为什么intmain(void)
比voidmain()
好?90%的情况下
long long round(float a) {
long long b = a;
if (a >= 0)
return a - b < 0.5 ? b : b + 1;
else
return b - a < 0.5 ? b : b - 1;
}