一、概况float 32位数据,8位用于存储指数,24位存储尾数。
基本参数从cfloat,climits(float.h)中查看:
#define FLT_DIG 6
#define FLT_MANT_DIG 24
#define FLT_MAX_10_EXP +38
#define FLT_MIN_10_EXP -38
分别表示,有效数字位数,尾数位数,指数最大,最小值。
二 、将一个float型转化为内存存储格式的步骤为:
(1)先将这个实数的绝对值化为二进制格式。
(2)将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边。
(3)从小数点右边第一位开始数出二十三位数字放入第22到第0位。
(4)如果实数是正的,则在第31位放入“0”,否则放入“1”。
(5)如果n 是左移得到的,说明指数是正的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”。
(6)如果n是左移得到的,则将n减去1后化为二进制,并在左边加“0”补足七位,放入第29到第23位。如果n是右移得到的或n=0,则将n化为二进制后在左边加“0”补足七位,再各位求反,再放入第29到第23位。
三、将一个内存存储的float二进制格式转化为十进制的步骤:
(1)将第22位到第0位的二进制数写出来,在最左边补一位“1”,得到二十四位有效数字。将小数点点在最左边那个“1”的右边。
(2)取出第29到第23位所表示的值n。当30位是“0”时将n各位求反。当30位是“1”时将n增1。
(3)将小数点左移n位(当30位是“0”时)或右移n位(当30位是“1”时),得到一个二进制表示的实数。
(4)将这个二进制实数化为十进制,并根据第31位是“0”还是“1”加上正号或负号即可。
float存储如下所示: 1位S ,8位E,23位F
其中F= 1.f(22)....f(0)
E= e - Bias(127)
X = F*2^E
四、代码与详细说明:
1. 其中S位占1bit,为bit31看,S位为0代表浮点数是正数,S位为1代表浮点数是负数。
2. E位占8bits,为bit23~bit30。E位代表2的N次方,但需要减去127(移位存储,后面再解释),比如说E位为87,那么E位的值为2(87-127)=9.094947017729282379150390625e-13。
3. F位占23bits,为bit0~bit22。F位是小数点后面的位数,其中bit22是2^-1=0.5,bit21是2^-2=0.25,以此类推,bit0为2^-23=0.00000011920928955078125。但F位里隐藏了一个1,也就是说F位所表示的值是1+(F位bit22~bit0所表示的数值),比如说F位是(0b)10100000000000000000001,只有bit22、bit20和bit0为1,那么F位的值为1+(2^-1+2^-20+2^-22),为1.5000011920928955。
代码与分析:
#include
using namespace std;
int main()
{
float a = 100;
cout<(b[i])<
100 ->float S = 0, e = 0x85 ,f = (计算需补1)100100(100 = 0x64 = 1100100 = 1.100100*2^6)
E = e - Bias = e - 2^7 +1= 6,F = 1.f = 0xC800
E*2^F = 100;
五、浮点数的精度
首先考虑float中的定义 FLT_EPSILON,意思为 “正float的0跨度值”,即满足 x+0.0 != 0.0 的最小正整数。
在32位系统中,该值等于 2^-23 约等于1.1920928955078125e-07 < 1.0e-6
该值计算可以通过:指数= 0 ,尾数取最小值来获得,即2^-23(f0取1其他取0)
由此,引申而来对float非0的判断或两数字相等判断,if(abs( a- b) < FLT_EPSILON)
六、指数取得38的原理
指数7位有效数字,指数原本-2^7-1 ~ 2^7由于bias的存储方式使得其存储内容为,-2^7~2^7 +1
然后2^7 + 1 相当于128,然后2^128,对应可以取得3.4*10^38,对应于S位的正负,因此float 的一个最大范围就是-3.4e+38~3.4e+38
而对应的最接近0,的非0数应该是,让指数取最负值,即-127,然后尾数取f=0,这个值大概为5.8e-39.
七、为何偏置非规范化值
1. 提供一种表示0值的方法,使用规格化M>=1而无法表示1,实际+0.0表示全0,M=f=0.符号位为1时,其他域为0,得到-0.0,偏置后使得只有一个0.
2. 那些非常接近0.0的数,提供了一种属性,逐渐溢出(gradual underflow),可能的数值分布均匀的接近0.0
八、特殊值
重要:指数全为1。
小数域全0,得到无穷,s=0,+无穷,s=1,-无穷。两大数相乘,或者除0,无穷可表示溢出结果。
小数域非0,NaN,一些运算的结果不能是实数或无穷,就会返回这样的NaN值,比如计算根号-1时候。(判断可以f!=f,或者isnan())
详解JavaScript跨域问题
1.什么是跨域
我们经常会在页面上使用ajax请求访问其他服务器的数据,此时,客户端会出现跨域问题.
跨域问题是由于javascript语言安全限制中的同源策略造成的.
简单来说,同源策略是指一段脚本只能读取来自同一来源的窗口和文档的属性,这里的同一来源指的是主机名、协议和端口号的组合.
例如:
URL
说明
是否允许通信
同一域名下
允许
同一域名下不同文件夹
允许
:8000/a.js
同一域名,不同端口
不允许
同一域名,不同协议
不允许
域名和域名对应ip
不允许
主域相同,子域不同
不允许
同一域名,不同二级域名(同上)
不允许(cookie这种情况下也不允许访问)
不同域名
不允许
解决方案
一、通过jsonp跨域
在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。
比如,有个a.html页面,它里面的代码需要利用ajax获取一个不同域上的json数据,假设这个json数据地址是,那么a.html中的代码就可以这样:
我们看到获取数据的地址后面还有一个callback参数,按惯例是用这个参数名,但是你用其他的也一样。当然如果获取数据的jsonp地址页面不是你自己能控制的,就得按照提供数据的那一方的规定格式来操作了。
因为是当做一个js文件来引入的,所以返回的必须是一个能执行的js文件,所以这个页面的php代码可能是这样的:
最终那个页面的输出结果是:
dosomething{['a','b','c']}
所以通过得到的js文件,就是我们之前定义的dosomething函数,并且它的参数就是我们需要的json数据,这样我们就跨域获得了我们需要的数据。
这样jsonp的原理就很清楚了,通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。
知道jsonp跨域的原理后我们就可以用js动态生成script标签来进行跨域操作了,而不用特意的手动的书写那些script标签。如果你的页面使用jquery,那么通过它封装的方法就能很方便的来进行jsonp操作了。
原理是一样的,只不过我们不需要手动的插入script标签以及定义回掉函数。jquery会自动 生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。
2、通过修改document.domain来跨子域
浏览器都有一个同源策略,其限制之一就是第一种方法中我们说的不能通过ajax的方法去请求不同源中的文档。 它的第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的。有一点需要说明,不同的框架之间(父子或同辈),是能够获取到彼此的window对象的,但蛋疼的是你却不能使用获取到的window对象的属性和方法(html5中的postMessage方法是一个例外,还有些浏览器比如ie6也可以使用top、parent等少数几个属性),总之,你可以当做是只能获取到一个几乎无用的window对象。比如,有一个页面,它的地址是 , 在这个页面里面有一个iframe,它的src是, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的:
这个时候,document.domain就可以派上用场了,我们只要把 和 这两个页面的document.domain都设成相同的域名就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:a.b.example.com 中某个文档的document.domain 可以设成a.b.example.com、b.example.com 、example.com中的任意一个,但是不可以设成 c.a.b.example.com,因为这是当前域的子域,也不可以设成baidu.com,因为主域已经不相同了。
在页面 中设置document.domain:
这样我们就可以通过js访问到iframe中的各种属性和对象了。
不过如果你想在 页面中通过ajax直接请求 页面,即使你设置了相同的document.domain也还是不行的,所以修改document.domain的方法只适用于不同子域的框架间的交互。如果你想通过ajax的方法去与不同子域的页面交互,除了使用jsonp的方法外,还可以用一个隐藏的iframe来做一个代理。原理就是让这个iframe载入一个与你想要通过ajax获取数据的目标页面处在相同的域的页面,所以这个iframe中的页面是可以正常使用ajax去获取你要的数据的,然后就是通过我们刚刚讲得修改document.domain的方法,让我们能通过js完全控制这个iframe,这样我们就可以让iframe去发送ajax请求,然后收到的数据我们也可以获得了。
3、使用window.name来进行跨域
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
比如:有一个页面a.html,它里面有这样的代码:
再看看b.html页面的代码:
a.html页面载入后3秒,跳转到了b.html页面,结果为:
我们看到在b.html页面上成功获取到了它的上一个页面a.html给window.name设置的值。如果在之后所有载入的页面都没对window.name进行修改的话,那么所有这些页面获取到的window.name的值都是a.html页面设置的那个值。当然,如果有需要,其中的任何一个页面都可以对window.name的值进行修改。注意,window.name的值只能是字符串的形式,这个字符串的大小最大能允许2M左右甚至更大的一个容量,具体取决于不同的浏览器,但一般是够用了。
上面的例子中,我们用到的页面a.html和b.html是处于同一个域的,但是即使a.html与b.html处于不同的域中,上述结论同样是适用的,这也正是利用window.name进行跨域的原理。
下面就来看一看具体是怎么样通过window.name来跨域获取数据的。还是举例说明。
比如有一个页面,需要通过a.html页面里的js来获取另一个位于不同域上的页面里的数据。
data.html页面里的代码很简单,就是给当前的window.name设置一个a.html页面想要得到的数据值。data.html里的代码:
那么在a.html页面中,我们怎么把data.html页面载入进来呢?显然我们不能直接在a.html页面中通过改变window.location来载入data.html页面,因为我们想要即使a.html页面不跳转也能得到data.html里的数据。答案就是在a.html页面中使用一个隐藏的iframe来充当一个中间人角色,由iframe去获取data.html的数据,然后a.html再去得到iframe获取到的数据。
充当中间人的iframe想要获取到data.html的通过window.name设置的数据,只需要把这个iframe的src设为就行了。然后a.html想要得到iframe所获取到的数据,也就是想要得到iframe的window.name的值,还必须把这个iframe的src设成跟a.html页面同一个域才行,不然根据前面讲的同源策略,a.html是不能访问到iframe里的window.name属性的。这就是整个跨域过程。
看下a.html页面的代码:
上面的代码只是最简单的原理演示代码,你可以对使用js封装上面的过程,比如动态的创建iframe,动态的注册各种事件等等,当然为了安全,获取完数据后,还可以销毁作为代理的iframe。网上也有很多类似的现成代码,有兴趣的可以去找一下。
通过window.name来进行跨域,就是这样子的。
4、使用HTML5中新引进的window.postMessage方法来跨域传送数据
window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。
调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 * 。
需要接收消息的window对象,可是通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
上面所说的向其他window对象发送消息,其实就是指一个页面有几个框架的那种情况,因为每一个框架都有一个window对象。在讨论第二种方法的时候,我们说过,不同域的框架间是可以获取到对方的window对象的,而且也可以使用window.postMessage这个方法。下面看一个简单的示例,有两个页面
我们运行a页面后得到的结果:
我们看到b页面成功的收到了消息。
结语:
除了以上几种方法外,还有flash、在服务器上设置代理页面等跨域方式,这里就不做介绍了。
以上四种方法,可以根据项目的实际情况来进行选择应用,个人认为window.name的方法既不复杂,也能兼容到几乎所有浏览器,这真是极好的一种跨域方法。
「软件测试的自我修养」每日精选推送,包括且不限于测试方法、技术、工具分享以及测试求职、笔试、面试心得,长按下方二维码关注还可能会偶遇Uncle C为你答疑解惑,随着时间的积累,相信同学们能和公众号一起茁壮成长,追上那个曾经被寄予厚望的自己.