[100分请教]float强制转化为long时遇到的怪问题

brinker 2005-07-25 03:06:02
代码很简单:
float f1,f2;
... // 计算f1和f2的值,在VC中用Watch观察发现,f1和f2的值都等于203.000
long l1 = (long)f1; // 这时候l1的值竟然不等于203,而是202
long l2 = (long)f2; // l2的值是正常的203

请问这是什么原因?如何避免
...全文
542 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
Start13 2005-07-25
  • 打赏
  • 举报
回复
楼主:
怎么在内存中看l1,l2的值啊?请教啊!
brinker 2005-07-25
  • 打赏
  • 举报
回复
谢谢各位,我把情况再详细说说,请各位看看能不能帮我,谢了
我现在做的东西是显示一幅地图,而地图是分块显示的,比如分为左右两块显示,从地图库中读取后要把地图中的经纬度转换为屏幕上的坐标才能显示出来,这时候问题就出来了,因为坐标转换的过程有少许误差,这样左边地图的右边界的屏幕坐标和右边地图的左边界的屏幕坐标(浮点数)就有一个比较小的(大约0.001)差值,而显示到屏幕的时候要转换成整数,这样就出现了我上面所说的问题。比如左边地图右边界(X)坐标是202.999 右边是203.000,又或者左边坐标是202.499,右边是202.500,直接转化成整数的时候就会有一个像素的误差,我的问题是怎么能尽可能少地避免这种误差。
sjjf 2005-07-25
  • 打赏
  • 举报
回复
把最后一位拼上 0x01,试一下

float f1,f2;
long l1 = (long)(f1|0x01);
long l2 = (long)(f2|0x01);
pcboyxhy 2005-07-25
  • 打赏
  • 举报
回复
仔细看下面的






#include <stdio.h>

int main(int argc, char *argv[])
{
float p = 5.1f;
int f = (int)(p*100);
printf("%d", f);
getch( );
return 0;
}

我想要输出 510,可是机器nnd居然输出509(竟然敢扣我工钱)。
到底是what's wrong。我上看下看,左看又看,看了又看,就是发现不了错误。
于是我试着把5.1改成5.5,一切正常啊。捣鼓了N个小时后猜想,莫非是浮点数的表示问题,
于是花了很久找到浮点数的机器表示方法,照着规定克隆操作了一下。(据说练过乾坤大
挪移的人什么招式都可以克隆)。

IEEE规定的浮点数的机器表示:

32位机器的 float 是4字节的,共32位。
第1位是符号位,接着8位指数位,接着23位基数位。
以5.1为例。

5 = 101 (2进制)

0.1 = 0.0 0011 0011 0011 0011 0011 0011 .....(无限循环)

所以 5.1 = 101.0 0011 0011 0011 0011 0011 0011 0011 0011 ...

5.1= 1.010 0011 0011 0011 0011 0011 0011 0011 0011 0011... * 2^2

因为第一位总是为1,如果是0,就移动小数点直到是非0的,所以第一位的1丢弃。
得到 010 0011 0011 0011 0011 0011 0011 0011 0011....
取23位 得到 0100 0110 0110 0110 0110 011

接着看指数部分
指数是2, 根据规定,指数统一+127再转换为无符号8位2进制数,
2+127=129 (1000 0001)

存储的时候指数部分存储在基数之前,这样就有31位了,
因为5.1是正的,所以符号为是0,存储在指数部分之前

这样就得到 0100 0000 1010 0011 0011 0011 0011 0011

我们来看一下机器上是否真的如此

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
float a=5.1;
int *i=&a;
printf("%x", *i);
system("PAUSE");
return 0;
}

40a33333
0100 0000 1010 0011 0011 0011 0011 0011

果真是一样的。

这个例子就说明了为什么浮点数有时存在这样的问题。
这个数化为10进制整数的时候,
由于不可能达到5.1(5.099..)
所以×100后截取了前面的值 509。

无奈吧,这个时候想要精确的浮点数的话,
只有自己写高精度算法了。
老夏Max 2005-07-25
  • 打赏
  • 举报
回复
一般浮点小数是用二进制小数表示的,和整数部分类似,小数也是2的幂次方,只不过幂次是负数而已。

例如:0.5(10)表示为0.1(2),通俗的说,就是2^-1,也就是1/2,同样:0.25(10)表示为0.01(2),即2^-2,也就是1/4。对于一个定长的小数,例如8位二进制小数,其实际的值就是所有位上的值相加而成,例如:

0.75(10)=0.11(2)=1/2+1/4

因此对于像0.1(10)这样的值需要很多值进行相加,设8位二进制小数,那么

0.1(10) 可表示为 0.00011001(2) = 1/16+1/32+1/256 = 0.09765625

或者

0.1(10) 可表示为 0.00011010(2) = 1/16+1/32+1/128 = 0.1015625

由于0.00011001和0.00011010之间已经没有可用的二进制表示了(相对于8位定长二进制小数来说),因此只能用这两个值来代替0.1,具体取那个根据实际的舍入规则确定,由此出现了误差。

所以,对于二进制小数来说,只有2的负整数次幂的小数才能够精确表示,否则就可能出现误差。
老夏Max 2005-07-25
  • 打赏
  • 举报
回复
这就是我所说的情况。只要加0.5然后再做就不会出错了!
老夏Max 2005-07-25
  • 打赏
  • 举报
回复
这就是我所说的情况。只要加0.5然后再做就不会出错了!
megaboy 2005-07-25
  • 打赏
  • 举报
回复
唉,楼主想到哪里去了,对于

float f1=203.0,f2=203.0;
long l1 = (long)f1;
long l2 = (long)f2;

这种情况来说,f1的值只会在203的附近,不会偏离到202.450012这么远的。对于202.450012这个值来说,只有f1=202.500这种情况才有可能,这时候,你应该给它加上个0.05l。
robinzsy 2005-07-25
  • 打赏
  • 举报
回复
四舍五入总要有一个分割点的,如果非要实现楼主的要求,可以先判断两数是否十分接近,如里两数之差绝对值小于0.01,如果十分接近,则使用其中一个数的舍入结果作为两数共同的舍入结果。
brinker 2005-07-25
  • 打赏
  • 举报
回复
试了一下,四舍五入到小数点后一位可以解决一部分问题,但是像这种数还是不行
比如:f1 = 302.450012
f2 = 302.449982
当然,四舍五入到小数点后两位就可以了,但是如果
f1 = 308.445007
f2 = 308.444977
又不行了
megaboy 2005-07-25
  • 打赏
  • 举报
回复
楼主的问题可以这样解决:

long l1 = (long)f1+0.5l;
long l2 = (long)f2+0.5l;
robinzsy 2005-07-25
  • 打赏
  • 举报
回复
好像还是不行。那就先四舍五入到小数点后一位,再四舍五入到整数吧。
robinzsy 2005-07-25
  • 打赏
  • 举报
回复
先用ceil()或floor()统一向上或向下取整,再转换成long
brinker 2005-07-25
  • 打赏
  • 举报
回复
谢谢各位,刚才看了一下内存,发现
f1大约等于202.999984
f2大约等于203.000010

现在的问题是如何能把它们都转化为203?
我试了一下用四舍五入,但是不好用,因为除了这种情况以外,还有一种
f1等于384.500030
f2等于384.499999
我想让转化后的结果是相等的,请问有什么方法?
robinzsy 2005-07-25
  • 打赏
  • 举报
回复
f1可能为202.9999999999....由于float类型的精度问题,故显示为203.00但实际上小于203,所以转换成long时,会被截断小数点后的部分。
PMsg 2005-07-25
  • 打赏
  • 举报
回复
如果f1和f2的值都等于203.000
那么后面long l1 l2 的值是一样的
你可以自己赋给f1 f2 203.000 然后强制转换看看啊
PMsg 2005-07-25
  • 打赏
  • 举报
回复
如果f1和f2的值都等于203.000
那么后面long l1 l2 的值是一样的
你可以自己赋给f1 f2 203.000 然后强制转换看看啊
oo 2005-07-25
  • 打赏
  • 举报
回复
浮点表示是不精确的。
你确认f1和f2都是 203.00 ?

64,439

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧