由一个浮点数问题引发的致命问题
2013-09-05 20:47
204 查看
最近因工程问题发现了浮点数在使用过程中的误区,在此与大家分享之
请看代码如下:
编译执行如下:
$ gcc floatCompare.c
$ ./a.out
发现其输出结果为:0.02 > 0.03-0.01
很明显,这样的结果是我们不可接受的。因为代码按我们所想就应打印0.02 <= 0.03 - 0.01才对。问题出在哪里呢?存储在内存中的float类型的变量并非就是0.02000000或者0.0300000。由此可参考如下代码:
为了方便讨论,我们把传递给FloatToShort函数的浮点数变量假定都为1529.16-1538.19之间的数。
我们直接取(浮点数-1500)*100的方法来看看会出现什么样的结果:
我们的本意是取浮点数去掉1500后扩大100倍的值,但令我们惊奇的是区区24个浮点数中,使用方法B时(即usWaveLen = (fWavelen - 1500)*100;),我们得到的数值有11个在意料之外。观察其到小数点后4位精度的数值发现有的浮点数本来为1529.94,其对应的4位精度表示居然是1529.9399,代码并未进行“四舍五入”从而得到想要的2994的结果。还有一种情况,浮点数本来为1530.33,其4位精度表示也是1530.3300,这样标准的数值在使用方法B后也没有得到想要的3033,却得到了3032。
由此可见,程序并未按照我们预期的想法进行。
有一个好的方法就是扩展其精度,我们只需小数点后两位的精度即可,那么使用方法A,我们先将浮点数扩大至原来的1000倍,然后看其个位数是否为大于5,小于5则按照正常的浮点数扩大100倍进行处理,大于5则说明在此基础上还需加1,不然仍会丢失一位小数。
按照这个方法我们测试如下:
由此可见,浮点数可能引发的问题在此得到了解决。
请看代码如下:
//FileName: floatCompare.c #include <stdio.h> int main(int argc, char *argv[]) { float fVal1, fVal2; float Excent = 0.03; fVal1 = 1592.15; fVal2 = 1592.17; if ((fVal2 - fVal1) <= (Excent - 0.01)) { printf("0.02 <= 0.03-0.01\n"); } else { printf("0.02 > 0.03-0.01\n"); } }
编译执行如下:
$ gcc floatCompare.c
$ ./a.out
发现其输出结果为:0.02 > 0.03-0.01
很明显,这样的结果是我们不可接受的。因为代码按我们所想就应打印0.02 <= 0.03 - 0.01才对。问题出在哪里呢?存储在内存中的float类型的变量并非就是0.02000000或者0.0300000。由此可参考如下代码:
//FileName: FloatToUShort.c #include <stdio.h> #define NUM 24 const float gfWaveLen[]; unsigned short FloatToShort(float fWavelen); int main() { int i = 0; unsigned short usVal = 0x0000; while (i < NUM) { usVal = FloatToShort(gfWaveLen[i]); printf("%.2f : %.4f : 0x%04x : %d\n",gfWaveLen[i], gfWaveLen[i], usVal, usVal); i ++; } } unsigned short FloatToShort(float fWavelen) { unsigned short usWaveLen = 0x0000; int iWaveLen = 0; float fWaveTmp = 0; #if defined(METHOD) //方法A fWaveTmp = fWavelen - 1500; iWaveLen = fWaveTmp * 1000; usWaveLen = ((iWaveLen %10) > 5) ? (fWaveTmp*100 + 1) : (fWaveTmp*100); #else usWaveLen = (fWavelen - 1500)*100; //方法B-这个地方有问题 #endif return usWaveLen; } const float gfWaveLen[NUM]= { 1529.16, 1529.55, 1529.94, 1530.33, 1530.72, 1531.12, 1531.51, 1531.90, 1532.29, 1532.68, 1533.07, 1533.47, 1533.86, 1534.25, 1534.64, 1535.04, 1535.43, 1535.82, 1536.22, 1536.61, 1537.00, 1537.40, 1537.79, 1538.19 };
为了方便讨论,我们把传递给FloatToShort函数的浮点数变量假定都为1529.16-1538.19之间的数。
我们直接取(浮点数-1500)*100的方法来看看会出现什么样的结果:
$ gcc FloatToUShort.c $ ./a.out 1529.16 : 1529.1600 : 0x0b64 : 2916 1529.55 : 1529.5500 : 0x0b8b : 2955 1529.94 : 1529.9399 : 0x0bb1 : 2993 @-1 1530.33 : 1530.3300 : 0x0bd8 : 3032 @-2 1530.72 : 1530.7200 : 0x0bff : 3071 @-3 1531.12 : 1531.1200 : 0x0c27 : 3111 @-4 1531.51 : 1531.5100 : 0x0c4f : 3151 1531.90 : 1531.9000 : 0x0c76 : 3190 1532.29 : 1532.2900 : 0x0c9d : 3229 1532.68 : 1532.6801 : 0x0cc4 : 3268 1533.07 : 1533.0699 : 0x0cea : 3306 @-5 1533.47 : 1533.4700 : 0x0d12 : 3346 @-6 1533.86 : 1533.8600 : 0x0d39 : 3385 @-7 1534.25 : 1534.2500 : 0x0d61 : 3425 1534.64 : 1534.6400 : 0x0d88 : 3464 1535.04 : 1535.0400 : 0x0db0 : 3504 1535.43 : 1535.4301 : 0x0dd7 : 3543 1535.82 : 1535.8199 : 0x0dfd : 3581 @-8 1536.22 : 1536.2200 : 0x0e25 : 3621 @-9 1536.61 : 1536.6100 : 0x0e4c : 3660 @-10 1537.00 : 1537.0000 : 0x0e74 : 3700 1537.40 : 1537.4000 : 0x0e9c : 3740 1537.79 : 1537.7900 : 0x0ec3 : 3779 1538.19 : 1538.1899 : 0x0eea : 3818 @11
我们的本意是取浮点数去掉1500后扩大100倍的值,但令我们惊奇的是区区24个浮点数中,使用方法B时(即usWaveLen = (fWavelen - 1500)*100;),我们得到的数值有11个在意料之外。观察其到小数点后4位精度的数值发现有的浮点数本来为1529.94,其对应的4位精度表示居然是1529.9399,代码并未进行“四舍五入”从而得到想要的2994的结果。还有一种情况,浮点数本来为1530.33,其4位精度表示也是1530.3300,这样标准的数值在使用方法B后也没有得到想要的3033,却得到了3032。
由此可见,程序并未按照我们预期的想法进行。
有一个好的方法就是扩展其精度,我们只需小数点后两位的精度即可,那么使用方法A,我们先将浮点数扩大至原来的1000倍,然后看其个位数是否为大于5,小于5则按照正常的浮点数扩大100倍进行处理,大于5则说明在此基础上还需加1,不然仍会丢失一位小数。
按照这个方法我们测试如下:
$ gcc FloatToUShort.c -DMETHOD $ ./a.out 1529.16 : 1529.1600 : 0x0b64 : 2916 1529.55 : 1529.5500 : 0x0b8b : 2955 1529.94 : 1529.9399 : 0x0bb2 : 2994 1530.33 : 1530.3300 : 0x0bd9 : 3033 1530.72 : 1530.7200 : 0x0c00 : 3072 1531.12 : 1531.1200 : 0x0c28 : 3112 1531.51 : 1531.5100 : 0x0c4f : 3151 1531.90 : 1531.9000 : 0x0c76 : 3190 1532.29 : 1532.2900 : 0x0c9d : 3229 1532.68 : 1532.6801 : 0x0cc4 : 3268 1533.07 : 1533.0699 : 0x0ceb : 3307 1533.47 : 1533.4700 : 0x0d13 : 3347 1533.86 : 1533.8600 : 0x0d3a : 3386 1534.25 : 1534.2500 : 0x0d61 : 3425 1534.64 : 1534.6400 : 0x0d88 : 3464 1535.04 : 1535.0400 : 0x0db0 : 3504 1535.43 : 1535.4301 : 0x0dd7 : 3543 1535.82 : 1535.8199 : 0x0dfe : 3582 1536.22 : 1536.2200 : 0x0e26 : 3622 1536.61 : 1536.6100 : 0x0e4d : 3661 1537.00 : 1537.0000 : 0x0e74 : 3700 1537.40 : 1537.4000 : 0x0e9c : 3740 1537.79 : 1537.7900 : 0x0ec3 : 3779 1538.19 : 1538.1899 : 0x0eeb : 3819
由此可见,浮点数可能引发的问题在此得到了解决。
相关文章推荐
- 一个乒乓球引发的一系列问题(穷举法)
- 一个问题引发的OOA&OOD讨论.
- 由一个#符号引发的一系列问题[转载]
- android:一个Open键引发的问题!!
- 由一个C++问题引发的讨论和思考(未完待续)
- 一个mfs引发的svn问题
- 一个由安装顺序引发的域登录问题
- Velocity Toolbox类的方法无法调用时引发的一个类型转换问题
- 关于char字符引发的一个问题
- 由一个简单的String c=a+b的Java问题引发一点想法 推荐
- js判断checkbox选中(记录一下,一个小问题引发的)
- PHP浮点数的一个常见问题的解答
- 一个空格引发的问题
- 一个.tar.gz文件引发的问题
- C++流实现内幕---由boost::lexical_cast引发的一个问题
- 由一个#符号引发的一系列问题
- InputStreamReader引发的一个编码问题
- 一个游戏引发的思考(概率问题)
- 由安卓开发中的一个坑引发的问题解决之道浅析