題目:將32位浮點(diǎn)數(shù) 01000010111011010000000000000000 轉(zhuǎn)換為十進(jìn)制格式
根據(jù)國(guó)際標(biāo)準(zhǔn)IEEE 754,任意一個(gè)二進(jìn)制浮點(diǎn)數(shù)V可以表示成下面的形式:

(1)(-1)^s表示符號(hào)位,當(dāng)s=0,V為正數(shù);當(dāng)s=1,V為負(fù)數(shù)。
(2)M表示有效數(shù)字,大于等于1,小于2。
(3)2^E表示指數(shù)位。
IEEE754標(biāo)準(zhǔn)中規(guī)定float單精度浮點(diǎn)數(shù)在機(jī)器中表示用 1 位表示數(shù)字的符號(hào),用 8 位來(lái)表示指數(shù),用23 位來(lái)表示尾數(shù),即小數(shù)部分。對(duì)于double雙精度浮點(diǎn)數(shù),用 1 位表示符號(hào),用 11 位表示指數(shù),52 位表示尾數(shù),其中指數(shù)域稱為階碼。
題目中的32位浮點(diǎn)數(shù),可以寫為 S+E+M 三部分的形式:0 10000101 11011010000000000000000

IEEE 754對(duì)有效數(shù)字M和指數(shù)E,還有一些特別規(guī)定。
- 有效數(shù)字 M ,1≤M<2,也就是說(shuō),M可以寫成1.xxxxxx的形式,其中xxxxxx表示小數(shù)部分。IEEE 754規(guī)定,在計(jì)算機(jī)內(nèi)部保存M時(shí),默認(rèn)這個(gè)數(shù)的第一位總是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的時(shí)候,只保存01,等到讀取的時(shí)候,再把第一位的1加上去。這樣做的目的,是節(jié)省1位有效數(shù)字。以32位浮點(diǎn)數(shù)為例,留給M只有23位,將第一位的1舍去以后,等于可以保存24位有效數(shù)字。
- 至于指數(shù)E,首先,E為一個(gè)無(wú)符號(hào)整數(shù)(unsigned int)。這意味著,如果E為8位,它的取值范圍為0-255;如果E為11位,它的取值范圍為0-2047。但是,我們知道,科學(xué)計(jì)數(shù)法中的E是可以出現(xiàn)負(fù)數(shù)的,所以IEEE 754規(guī)定,E的真實(shí)值必須再減去一個(gè)中間數(shù),對(duì)于8位的E,這個(gè)中間數(shù)是127;對(duì)于11位的E,這個(gè)中間數(shù)是1023。
指數(shù)E還可以再分成三種情況:
E不全為0或不全為1。
這時(shí),浮點(diǎn)數(shù)就采用上面的規(guī)則表示,即指數(shù)E的計(jì)算值減去127(或1023),得到真實(shí)值,再將有效數(shù)字M前加上第一位的1。
E全為0。
這時(shí),浮點(diǎn)數(shù)的指數(shù)E等于1-127(或者1-1023),有效數(shù)字M不再加上第一位的1,而是還原為0.xxxxxx的小數(shù)。這樣做是為了表示±0,以及接近于0的很小的數(shù)字。
E全為1。
這時(shí),如果有效數(shù)字M全為0,表示±無(wú)窮大(正負(fù)取決于符號(hào)位s);如果有效數(shù)字M不全為0,表示這個(gè)數(shù)不是一個(gè)數(shù)(NaN)。
舉例來(lái)說(shuō),
十進(jìn)制的5.0,寫成二進(jìn)制是101.0,相當(dāng)于1.01×2^2。那么,按照上面V的格式,可以得出s=0,M=1.01,E=2。
十進(jìn)制的-5.0,寫成二進(jìn)制是-101.0,相當(dāng)于-1.01×2^2。那么,s=1,M=1.01,E=2。
public static void main(String[] args){
String binaryString="01000010111011010000000000000000";
double result; //存放結(jié)果
String sign = binaryString.substring(0, 1); //得到符號(hào)位
String exponent = binaryString.substring( 1, 9 ); //得到階碼
int expint = Integer.parseInt(exponent, 2); //指數(shù)轉(zhuǎn)換為十進(jìn)制
int mobit = expint - 127; //得到實(shí)際的階碼
Double d = Math.pow(2,mobit); //以2為底求值
System.out.println(d);
String last = binaryString.substring(9); //得到尾數(shù)
System.out.println(last);
double lastRes = 0D; //存放尾數(shù)的結(jié)果
for(int i=0; i<last.length(); i++) {
char b = last.charAt(i);
if(b == '1') {
lastRes += Math.pow(2, -(i + 1)); //尾數(shù)的計(jì)算
}
}
result = d * (sign.equals("1") ? -1 : 1) * (1 + lastRes);
System.out.println(result);
}
JS 中的最大安全整數(shù)是多少?
JS 中所有的數(shù)字類型,實(shí)際存儲(chǔ)都是通過(guò) 8 字節(jié) double 浮點(diǎn)型 表示的。浮點(diǎn)數(shù)并不是能夠精確表示范圍內(nèi)的所有數(shù)的, 雖然 double 浮點(diǎn)型的范圍看上去很大: 2.23x10^(-308) ~ 1.79x10^308。 可以表示的最大整數(shù)可以很大,但能夠精確表示,使用算數(shù)運(yùn)算的并沒(méi)有這么大。
它其實(shí)連這樣的簡(jiǎn)單加法也會(huì)算錯(cuò):
console.log(0.1 + 0.2)
//output: 0.30000000000000004
所以在 js 中能夠安全使用的有符號(hào) 安全 大整數(shù)(注意這里是指能夠安全使用,進(jìn)行算數(shù)運(yùn)算的范圍),并不像其他語(yǔ)言在 64 位環(huán)境中那樣是:
2^63 - 1;//9223372036854775807
而是
Math.pow(2, 53) - 1 // 9007199254740991
JS 的最大和最小安全值可以這樣獲得:
console.log(Number.MAX_SAFE_INTEGER); //9007199254740991
console.log(Number.MIN_SAFE_INTEGER); //-9007199254740991
通過(guò)下面的例子,你會(huì)明白為什么大于這個(gè)值的運(yùn)算是不安全的:
var x = 9223372036854775807;
console.log(x === x + 1);// output: true
console.log(9223372036854775807 + 1000); //output: 9223372036854776000
這些運(yùn)算都是錯(cuò)誤的結(jié)果, 因?yàn)樗鼈冞M(jìn)行的都是浮點(diǎn)數(shù)運(yùn)算會(huì)丟失精度。
為什么是這個(gè)值?
double 浮點(diǎn)數(shù)結(jié)構(gòu)如下:
1 位符號(hào)位
11 位指數(shù)位
52 位尾數(shù)位
使用 52 位表示一個(gè)數(shù)的整數(shù)部分,那么最大可以精確表示的數(shù)應(yīng)該是 2^52 - 1 才對(duì), 就像 64 位表示整數(shù)時(shí)那樣: 2^63 - 1 (去掉 1 位符號(hào)位)。 但其實(shí)浮點(diǎn)數(shù)在保存數(shù)字的時(shí)候做了規(guī)格化處理,以 10 進(jìn)制為例:
20*10^2 => 2*10^3 //小數(shù)點(diǎn)前只需要保留 1 位數(shù)
對(duì)于二進(jìn)制來(lái)說(shuō), 小數(shù)點(diǎn)前保留一位, 規(guī)格化后始終是 1.***, 節(jié)省了 1 bit,這個(gè) 1 并不需要保存。
解決浮點(diǎn)數(shù)溢出的辦法
-
使用toFixed方法返回一個(gè)以定點(diǎn)表示法表示的數(shù)字的字符串形式
- 調(diào)用一個(gè)處理函數(shù)
function overflow(a, h, b) {
var _a = a.toString().split(".");
if (_a.length == 1) {
_a = 0;
} else {
_a = _a[1].length;
}
var _b = b.toString().split(".");
if (_b.length == 1) {
_b = 0;
} else {
_b = _b[1].length;
}
if (_b > _a)_a = _b;
_b = "1";
for (; _a > 0; _a--) {
_b = _b + "0";
}
switch (h) {
case"+":
return (a * _b + b * _b) / _b;
break;
case"-":
return (a * _b - b * _b) / _b;
break;
case"*":
return ((a * _b) * (b * _b)) / (_b * _b);
break;
default:
return 0;
}
}
var a = 0.1;
var b = 0.2;
console.log(overflow(a, "+", b));//0.3
參考文章:http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html
