2018第一篇博客,good lucky~~~
0x000 遇到的问题
- 写这篇博客的缘由是上次刷
OJ
时,碰到的一个问题.
在太平洋的一个小岛上,岛民想要建立一个环岛的堤坝,我们可以将小岛简化为一个二维平面,你需要使用K条边(这些边要么是水平或者垂直长度为1的边,要么是45度倾斜的长度为sqrt(2)的边)围成一个多边形,多边形的顶点必须位于整点,然后要让围成的多边形面积最大,你需要求出最大面积是多少。
- 推出递推公式后,在奇数条件下需要减去一个
0.5
,当n
足够大时,我发现用答案去减这个0.5
后答案不会变,用了各种方法都没有能够让答案变化0.5
.虽然最后用JAVA
解决了此题,但是这个问题一直没有得到解决.
0x001 发现曙光
最近这些天在复习计算机组成原理,在看到数据的表示与运算这一章的时候,发现浮点数的四则运算是通过对阶,让小的数字通过右移操作与大的数字的阶码相同,然后通过尾数相加所得。
我便有了一个猜想,是不是因为数字的阶码相差过大而导致
0.5
通过位移操作而变为了0
?
0x010 推导
上图是
IEEE 754
的标准,我们可以看到在计算机中64
位浮点数的数符单独占1
位,阶符和阶码占11
位,尾数占52
位,而尾数的第一位是隐藏位.在
IEEE 754
中,对于0.5
,其符号位为0
,指数(阶符和阶码部分)为-1
,规格化后的尾数为1.0
,在IEEE 754
中的尾数为M = 1000 ... 0000(加上隐藏位共53位,以原码进行表示)
.故猜想:当其与一个阶码为
52
的数字相加时,由小阶向大阶看齐的规则,M
向右移动53
位之后变成了0000 ... 0000(共53位)
.2^52 = 1000 0000 ... 0000(共53位)
1000 0000 ... 0000 + 0000 0000 ... 0000 = 1000 0000 ... 0000
- 故猜想当数字达到
2^52
时,加上0.5
不会改变其值.
0x011 通过二分法进行验证
|
|
运行结果为
4503599627370496
- 证明我们的猜想是正确的.
0x100 未解决的一个小细节
- 通过推导我们证明了
2^52
加上0.5
不会改变其值.但是在测试中,当取值属于[2^52+1,2^52+10)
时,每次加上0.5
或者减去0.5
之后的答案会多1
或者少1
,而不是上面所说的不变.我猜测是由于0.5
进行右移的时候当1
被移掉时应用了0舍1进
的方式,在这段区间内产生了多1
或者少1
的现象,当然也有可能是C语言内部的问题,除此细节外,其余的大于2^52
的数都与我的猜想一致.123456int main(){LL A = 4503599627370496+1;printf("%.1lf %.1lf\n",A-0.5,A+0.5);return 0;}
4503599627370496.0 4503599627370498.0
0x101 后记&参考
- 第一次用硬件知识解释软件中遇到的问题,收获颇多.
《计算机组成原理》
作为计算机专业唯一一本关于硬件的书,非常值得去学习,不然我们只会把在软件中碰到的问题归结与玄学之流.这显然是不符合理工科的探索精神的.- IEEE754浮点数的表示方法