﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-jjsee-文章分类-C++</title><link>http://www.cppblog.com/jianjianxiaole/category/12903.html</link><description /><language>zh-cn</language><lastBuildDate>Sun, 26 Dec 2010 08:31:45 GMT</lastBuildDate><pubDate>Sun, 26 Dec 2010 08:31:45 GMT</pubDate><ttl>60</ttl><item><title>浮点数的表示范围</title><link>http://www.cppblog.com/jianjianxiaole/articles/float.html</link><dc:creator>望见</dc:creator><author>望见</author><pubDate>Sun, 26 Dec 2010 07:44:00 GMT</pubDate><guid>http://www.cppblog.com/jianjianxiaole/articles/float.html</guid><wfw:comment>http://www.cppblog.com/jianjianxiaole/comments/137500.html</wfw:comment><comments>http://www.cppblog.com/jianjianxiaole/articles/float.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jianjianxiaole/comments/commentRss/137500.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jianjianxiaole/services/trackbacks/137500.html</trackback:ping><description><![CDATA[<div>一个浮点数（Floating Point Number）由三个基本成分构成：符号（Sign）、阶码（Exponent）和尾数（Mantissa）。<br>　　通常，可以用下面的格式来表示浮点数：
<div align=center>
<table border=1 cellSpacing=0 cellPadding=2 width=400 align=center>
    <tbody>
        <tr>
            <td vAlign=top width=133>S</td>
            <td vAlign=top width=133>P</td>
            <td vAlign=top width=133>M</td>
        </tr>
    </tbody>
</table>
</div>
<div align=left>　　其中S是符号位，P是阶码，M是尾数。<br>　　根据IEEE（美国电气和电子工程师学会）754标准中的定义，单精度（Single Precision）浮点数是32位（即4字节）的，双精度（Double Precision）浮点数是64位（即8字节）的。两者的S、P、M所占的位数以及表示方法由下表可知：</div>
<div align=center>
<table border=1 cellSpacing=0 cellPadding=2 width=400 align=center>
    <tbody>
        <tr>
            <td vAlign=top width=66></td>
            <td vAlign=top width=66>S</td>
            <td vAlign=top width=66>P</td>
            <td vAlign=top width=66>M</td>
            <td vAlign=top width=66>表示公式</td>
            <td vAlign=top width=66>偏移量</td>
        </tr>
        <tr>
            <td vAlign=top width=66>
            <p>单精度浮点数</p>
            </td>
            <td vAlign=top width=66>
            <p>1（第31位）</p>
            </td>
            <td vAlign=top width=66>
            <p>8（30到23位）</p>
            </td>
            <td vAlign=top width=66>
            <p>23（22到0位）</p>
            </td>
            <td vAlign=top width=66>
            <p>(-1)^S*2(P-127)*1.M</p>
            </td>
            <td vAlign=top width=66>127</td>
        </tr>
        <tr>
            <td vAlign=top width=66>
            <p>双精度浮点数</p>
            </td>
            <td vAlign=top width=66>
            <p>1（第63位）</p>
            </td>
            <td vAlign=top width=66>
            <p>11（62到52位）</p>
            </td>
            <td vAlign=top width=66>
            <p>52（51到0位）</p>
            </td>
            <td vAlign=top width=66>
            <p>(-1)^S*2(P-1023)*1.M</p>
            </td>
            <td vAlign=top width=66>1023</td>
        </tr>
    </tbody>
</table>
</div>
<div align=left>　　其中S是符号位，只有0和1，分别表示正负。<br>　　P是阶码，通常使用移码表示（移码和补码只有符号位相反，其余都一样。对于正数而言，原码、反码和补码都一样；对于负数而言，补码就是其绝对值的原码全部取反，然后加1）。阶码可以为正数，也可以为负数，为了处理负指数的情况，实际的指数值按要求需要加上一个偏差（Bias）值作为保存在指数域中的值，单精度数的偏差值为127，双精度数的偏差值为1023。例如，单精度的实际指数值0在指数域中将保存为127，而保存在指数域中的64则表示实际的指数值-63，偏差的引入使得对于单精度数，实际可以表达的指数值的范围就变成-127到128之间（包含两端）。<br>　　M为尾数，其中单精度数为23位长，双精度数为52位长。IEEE标准要求浮点数必须是规范的。这意味着尾数的小数点左侧必须为1，因此在保存尾数的时候，可以省略小数点前面这个1，从而腾出一个二进制位来保存更多的尾数。这样实际上用23位长的尾数域表达了24位的尾数。例如对于单精度数而言，二进制的1001.101（对应于十进制的9.625）可以表达为1.001101 &#215; 23，所以实际保存在尾数域中的值为00110100000000000000000，即去掉小数点左侧的1，并用0在右侧补齐。<br>　　根据标准要求，无法精确保存的值必须向最接近的可保存的值进行舍入，即不足一半则舍，一半以上（包括一半）则进。不过对于二进制浮点数而言，还多一条规矩，就是当需要舍入的值刚好是一半时，不是简单地进，而是在前后两个等距接近的可保存的值中，取其中最后一位有效数字为零者。<br>　　据以上分析，IEEE 754标准中定义浮点数的表示范围为：</div>
<div align=center>
<table border=1 cellSpacing=0 cellPadding=2 width=400 align=center>
    <tbody>
        <tr>
            <td vAlign=top width=133></td>
            <td vAlign=top width=133>
            <p>二进制（Binary）</p>
            </td>
            <td vAlign=top width=133>
            <p>十进制（Decimal）</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=133>
            <p>单精度浮点数</p>
            </td>
            <td vAlign=top width=133>
            <p>&#177; (2-2^-23) &#215; 2127</p>
            </td>
            <td vAlign=top width=133>
            <p>~ &#177; 10^38.53</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=133>
            <p>双精度浮点数</p>
            </td>
            <td vAlign=top width=133>
            <p>&#177; (2-2^-52) &#215; 21023</p>
            </td>
            <td vAlign=top width=133>
            <p>~ &#177; 10^308.25</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div align=left>　　浮点数的表示有一定的范围，超出范围时会产生溢出（Flow），一般称大于绝对值最大的数据为上溢（Overflow），小于绝对值最小的数据为下溢（Underflow）。<br><strong>二、浮点数的表示约定<br></strong>　　单精度浮点数和双精度浮点数都是用IEEE 754标准定义的，其中有一些特殊约定，例如：<br>　　1、当P=0，M=0时，表示0。<br>　　2、当P=255，M=0时，表示无穷大，用符号位来确定是正无穷大还是负无穷大。<br>　　3、当P=255，M&#8800;0时，表示NaN（Not a Number，不是一个数）。<br><strong>三、非规范浮点数<br>　　</strong>当两个绝对值极小的浮点数相减后，其差值的指数可能超出允许范围，最终只能近似为0。为了解决此类问题，IEEE标准中引入了非规范（Denormalized）浮点数，规定当浮点数的指数为允许的最小指数值时，尾数不必是规范化（Normalized）的。有了非规范浮点数，去掉了隐含的尾数位的制约，可以保存绝对值更小的浮点数。而且，由于不再受到隐含尾数域的制约，上述关于极小差值的问题也不存在了，因为所有可以保存的浮点数之间的差值同样可以保存。<br>　　根据IEEE 754标准中的定义，规范和非规范浮点数的表示范围可归纳为下表：</div>
<div align=center>
<table border=1 cellSpacing=0 cellPadding=2 width=400 align=center>
    <tbody>
        <tr>
            <td vAlign=top width=100></td>
            <td vAlign=top width=100>
            <p>规范浮点数</p>
            </td>
            <td vAlign=top width=100>
            <p>非规范浮点数</p>
            </td>
            <td vAlign=top width=100>
            <p>十进制近似范围</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=100>
            <p>单精度浮点数</p>
            </td>
            <td vAlign=top width=100>
            <p>&#177; 2^-149 至 (1-2^-23)*2^-126</p>
            </td>
            <td vAlign=top width=100>
            <p>&#177; 2^-126 至 (2-2^-23)*2^127</p>
            </td>
            <td vAlign=top width=100>
            <p>&#177; ~10^-44.85 至 ~10^38.53</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=100>
            <p>双精度浮点数</p>
            </td>
            <td vAlign=top width=100>
            <p>&#177; 2^-1074 至 (1-2^-52)*2^-1022</p>
            </td>
            <td vAlign=top width=100>
            <p>&#177; 2^-1022 至 (2-2^-52)*2^1023</p>
            </td>
            <td vAlign=top width=100>
            <p>&#177; ~10^-323.3 至 ~10^308.3</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div align=left><strong>四、与IEEE 754相关的标准<br></strong>　　本文的结论基于IEEE 754标准，另外一个标准是IEEE 854，这个标准是关于十进制浮点数的，但没有规定具体格式，所以很少被采用。另外，从2000年开始，IEEE 754开始修订，被称为IEEE 754R，目的是融合IEEE 754和IEEE 854标准。该标准在浮点格式方面的修订有：1、加入了16位和128位的二进制浮点数格式；2、加入了十进制浮点数格式，采用了IBM公司提出的格式。</div>
</div><img src ="http://www.cppblog.com/jianjianxiaole/aggbug/137500.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jianjianxiaole/" target="_blank">望见</a> 2010-12-26 15:44 <a href="http://www.cppblog.com/jianjianxiaole/articles/float.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C语言二重指针的运用</title><link>http://www.cppblog.com/jianjianxiaole/articles/106489.html</link><dc:creator>望见</dc:creator><author>望见</author><pubDate>Tue, 26 Jan 2010 14:27:00 GMT</pubDate><guid>http://www.cppblog.com/jianjianxiaole/articles/106489.html</guid><wfw:comment>http://www.cppblog.com/jianjianxiaole/comments/106489.html</wfw:comment><comments>http://www.cppblog.com/jianjianxiaole/articles/106489.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jianjianxiaole/comments/commentRss/106489.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jianjianxiaole/services/trackbacks/106489.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>『摘要』本文主要通过实例展示</span><span>C/C++</span><span>中二重指针的用法和用途，对于诸如二叉树等递归定义的数据结构有一定的指导作用</span><span> </span><span>。</span></p>
<p><span>【关键字】：<span>C/C++</span>、二重指针、递归</span></p>
<p><span>本人最近想实现一个<span>B+</span>树，虽然对<span>B+</span>树的理论有一定的认识，但由于考研花去大量时间复习功课，对<span>C</span>的一些细节有所遗忘，因此决定从二叉树的实现开始。但刚写完二叉树的创建函数且在编译通过之后，调试时却出现了问题。二叉树是一种递归定义的数据结构，因此其创建函数也必定是递归的。其创建函数描述如下：<br></span></p>
<p><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/jianjianxiaole/3.JPG" width=505 height=271></p>
<p><span>&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>图<span>1&nbsp;</span>二叉树创建函数</span></p>
<p><span>理论上没有错误，同时在创建二叉树时也能成功，但通过本人编写的先序遍历该二叉树时，却显示<span>0</span>，即空的二叉树，相关操作代码如下：</span></p>
<p><span>//</span><span>具体的操作步骤</span></p>
<p><span>TREE tree = 0; //TREE</span><span>为二叉树结构体的指针型别</span></p>
<p><span>createBinaryTree(tree);</span></p>
<p><span>//</span><span>先序遍历</span></p>
<p><span>由于结果错误，因此开始调试，通过观察发现当<span>createVinartTree(tree)</span>操作完成后<span>tree</span>的属性仍然为<span>0</span>，即仍然<span>tree=0</span>。后来又通过仔细分析，发现问题出在函数的参数上。虽然该函数传入的是指针型别，属于实参，但是该函数内部主要以原指针作为操作对象，因此相对于指针来说，传入的只是结构体指针的形参。所以，在函数内部操作的只是一个副本，因此二叉树的创建失败。</span></p>
<p><span>为了解决这个问题，笔者决定验证自己的想法是否正确，于是写了如下一段测试代码<span>,</span>代码如下：</span></p>
<p><span>//</span><span>版本<span>1</span></span></p>
<p><span>typedef struct student {</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>char* name;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>int age;</span></p>
<p><span>}Student,*STUDENT;</span></p>
<p>&nbsp;</p>
<p><span>void getInstance(STUDENT s);</span></p>
<p>&nbsp;</p>
<p><span>int main(){</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>Student* st = 0;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>printf("</span><span>指针<span>st</span>的地址<span>=%d\n",&amp;st);</span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>getInstance(st);</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>printf("st</span><span>新实例的地址<span>=%d\n",st);</span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>return 1;</span></p>
<p><span>}</span></p>
<p><span>void getInstance(STUDENT s){</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>printf("</span><span>指针<span>S</span>的地址<span> =%d\n",&amp;s);</span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>s = (Student*)malloc(sizeof(student));</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>printf("s</span><span>新实例的地址<span>=%d\n",s);</span></span></p>
<p><span>}</span></p>
<p><span>这个函数的传值形式与之前二叉树的函数差不多，因此可以类比，具体运行之后得到的记过是（如图）：</span></p>
<p><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/jianjianxiaole/1.JPG" width=376 height=106></p>
<p><span>图<span>2 </span>版本<span>1</span>的运行结果</span></p>
<p>&nbsp;</p>
<p><span>结果很显然，指针传入函数之后产生了一个新的副本，对副本的任何操作，都不会影响到原指针指向的结构体，与二叉树创建失败类似，目的指针的副本指向了目标结构体，产生了内存泄漏。</span></p>
<p><span>这个问题的解决也十分的简单，将结构体的指针作为实参传入函数即可，这样就可以直接操作目标指针，也就不会出现错误的结果。运用二重指针可以轻松实现，具体修改如下：</span></p>
<p><span>//</span><span>版本<span>2</span></span></p>
<p><span>typedef struct student {</span></p>
<p><span>&nbsp;char* name;</span></p>
<p><span>&nbsp;int age;</span></p>
<p><span>}Student<span>,<strong>**STUDENT</strong>;</span></span></p>
<p>&nbsp;</p>
<p><span>void getInstance(STUDENT s);</span></p>
<p>&nbsp;</p>
<p><span>int main(){</span></p>
<p><span>&nbsp;Student* st = 0;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>printf("</span><span>指针<span>st</span>的地址<span>=%d\n",&amp;st);</span></span></p>
<p><span>&nbsp;&nbsp;<strong>getInstance(&amp;st);</strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>printf("st</span><span>新实例的地址<span>=%d\n",st);</span></span></p>
<p><span>&nbsp;return 1;</span></p>
<p><span>}</span></p>
<p><span>void getInstance(STUDENT s){</span></p>
<p><span>&nbsp;printf("</span><span>指针<span>S</span>的地址<span> =%d\n",s);</span></span></p>
<p><span>&nbsp;<strong>*s = (Student*)malloc(sizeof(student));</strong></span></p>
<p><span>&nbsp;<strong>printf("s</strong></span><strong><span>新实例的地址<span>=%d\n",*s);</span></span></strong></p>
<p><span>}</span></p>
<p><span>修改部分如黑体部分所示，具体的运行结果如下图：</span></p>
<p><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/jianjianxiaole/2.JPG" width=365 height=105></p>
<p><span>&nbsp;</span><span>图<span>3 </span>版本<span>2</span>的运行结果</span></p>
<p><span>测试成功，函数内部操作的指针就是实际传入的指针，即通过二重指针实现了目标操作指针的实参传递，因此能达到预想的结果。这也说明了二重指针的实际用处。</span></p>
<img src ="http://www.cppblog.com/jianjianxiaole/aggbug/106489.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jianjianxiaole/" target="_blank">望见</a> 2010-01-26 22:27 <a href="http://www.cppblog.com/jianjianxiaole/articles/106489.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>