博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从44.556677想到的
阅读量:5172 次
发布时间:2019-06-13

本文共 2272 字,大约阅读时间需要 7 分钟。

如下代码:

float a = 44.556677f;printf("%f\n", a);

得到的输出是什么?并不是44.556677,而是44.556679。浮点数这么坑吗?为什么不一样呢?

浮点数的二进制表示,一种是手算,另一种是直接格式转换然后输出。

手算的,我看的这里的

发现44.556677算出来是42323a09,44.556679是42323a0a,两个不一样。计算过程如下:

495962-20181219133053007-2108479053.jpg

强转格式输出的,我看的这里的:

44.556677和44.556679的输出都是42323a0a

对应的代码:

//show_float.c #include 
unsigned int float2hexRepr(float* a){ unsigned int c; c = ((unsigned int*)a)[0]; return c;}int main(){ float a = 44.556677f; printf("print out 44.556677 but get: %f\n", a); printf("44.556677's hex: %x\n", float2hexRepr(&a)); float b = 44.556679f; printf("44.556679's hex: %x\n", float2hexRepr(&b)); return 0;}

编译运行结果:

print out 44.556677 but get: 44.55667944.556677's hex: 42323a0a44.556679's hex: 42323a0a

也就是:44.556677f这个float32类型的数据,在内存中的16进制,应该是0x42320a0a而不是0x42320a09。为什么呢?

因为10进制的0.556677转换为2进制的小数时,多次乘以2仍然得不到1.0,但是IEEE 754标准规定的位数又有限,这就产生了真实值和存储值的误差。为了尽量减小误差就需要考虑舍入(rounding)。

具体怎么rounding呢?实际上IEEE 754规定了4种rounding方式,但只有一个默认方式:rounding to neareast,也就是舍入到偶数,那么转换得到的尾数的2进制表示的最后4位,也就是1001,它的最后一位是奇数,要变成1010。(但是为什么不是1000呢)

495962-20181219133020378-319441362.jpg

上图来自《CSAPP》。然而感觉还是没有说清楚。

又看了wikipedia[],总算明白了。IEEE 754其实规定了5个rounding rule,默认是:Round to nearest, ties to even – rounds to the nearest value; if the number falls midway, it is rounded to the nearest value with an even least significant digit; this is the default for binary floating point and the recommended default for decimal.

完整计算:

对于44.556677,整数部分44的2进制表示是101100;小数部分的2进制表示,先假设为F,那么整个44.556677的2进制科学记数法表示为1.01100F * 2^5。

然后考虑这个F的计算:尾数部分总共23位,其中作为整数部分的44的产生了.01100这5位,作为尾数的前5位;

剩余18位则由0.556677转换为2进制小数而得到:每次乘以2,然后记录下整数部分,拿小数部分再进入下一次计算,而因为0.556677本身的原因,多次乘以2还是无法得到1.0,也就是“乘2”的次数超过了18次,但是还没有停止的意思。。

我们来看它产生的19位都是什么:1000111010000010011 (为什么要19位,因为第19位不为0,我们当前需要18位,这就产生了截断:去掉了第19位和更后面的位,影响了精度。为了让精度下降的更少些,就需要舍入,也就是rounding)。

  • 我们看到第19位是1,那么第18位从1变成0同时第17位从0变成1,这样精度的损失最多是第20位级别的;
  • 而如果第18位变成0,则精度损失是第18位级别的;
  • 而如果第18位不变,则精度损失是第19位级别的

显然,只有第17、18位从01变成10,才能由最小的精度损失。这就是rounding to nearest的第一种意思。(另一种意思是:如果被考虑的数字的二进制表示存在精度损失,并且恰好由两个跟它的值最接近的2进制表示,那么取偶数的那个表示)。

参考:

这个网址计算规则是有问题的,不是遵循IEEE 754标准的。 比如44.556677,IEEE 754标准下应该是0x42323a0a,而不是那个网址算出来的0x42323a09。原因是尾数存在精度损失,需要舍入(rounding),但是这个网址里没做rounding。rounding默认规则是rounding to neareast,也就是尾数需要改为“让精度损失最小的表示”。

rounding规则在这里:

这个网址的转换才是靠谱的。

转载于:https://www.cnblogs.com/zjutzz/p/10140559.html

你可能感兴趣的文章
把二元查找树转变成排序的双向链表
查看>>
input与select 设置相同宽高,在浏览器上却显示不一致,不整齐
查看>>
NUGET常用命令
查看>>
CentOs下Apache+Python+Django+mod_wsgi环境搭建
查看>>
java基础知识总结(3)
查看>>
spark配置
查看>>
数据仓库 - 3.数据仓库基本概念
查看>>
自定义树莓派的显示分辨率
查看>>
sql full left right inner cross 基础
查看>>
SpringBoot + Mybaties的逆向工程有数据库生成domain的过程
查看>>
Android控件TextView之跑马灯功能问题记录
查看>>
国外漂亮质感网页设计作品欣赏 转
查看>>
SVN学习总结
查看>>
cognos安装 win7+Sqlserver08SP1
查看>>
Java的21个技术点和知识点归纳
查看>>
安卓中Paint类和Canvas类的方法汇总
查看>>
提供openssl -aes-256-cbc兼容加密/解密的简单python函数
查看>>
数据结构-表
查看>>
【转载】下一代Web应用模型:Progressive Web App
查看>>
gridControl 部分属性
查看>>