讲解部分今天先不消化了,感谢陈浩老师的讲解,下面是陈浩老师的原话:
下面,让我来试着解释一下浮点数的那三段表示什么意思。
- 第一段符号位。对于这一段,我相信应该没有人不能理解。
- 第二段指数位。什么叫指数?也就是说,对于任何数 x,其都可以找到一个 n,使得 2n<=x<=2n+1。比如:对于 3 来说,因为 2 < 3 < 4,所以 n=1。而浮点数的这个指数为了要表示 0.00x 的小数,所以需要有负数,这 8 个 bits 本来可以表示 0-255。为了表示负的,取值要放在 [-127,128] 这个区间中。这就是为什么我们在上面的公式中看到的 2(E−127) 这一项了。也就是说,n=E−127,如果 n=1,那么 E 就是 128 了。
- 第三段尾数位。也就是小数位,但是这里叫偏移量可能好一些。这里的取值是从[ 0 - 223]中。你可以认为,我们把一条线分成 223 个线段,也就是 8388608 个线段。也就是说,把 2n 到 2n+1 分成了 8388608 个线段。而存储的 M 值,就是从 2n 到 x 要经过多少个段。这要计算一下,2n 到 x 的长度占 2n 到 2n+1 长度的比例是多少。
今天主要是使用 JS 实现。
下面是准备的函数,第一个是将正整数转换成二进制的字符串;第二个是按要求补零操作。
/**
* 求一个正数的二进制
* @param {number} d 正数
* @returns {string} 二进制
*/
function uitoa(d) {
let strN = "";
const _uitoa = (d) => {
if (d === 0) {
return;
}
strN = (d % 2) + strN;
_uitoa(Math.floor(d / 2));
};
_uitoa(d);
return strN;
}
/**
* 按给定的 n 将传入的二进制补上 0
* @param {string} bStr 要补零的二进制
* @param {number} n 要求的位数
* @returns {string} 经过补零操作以后的二进制
*/
function zeroFill(bStr, n) {
let bArr = bStr.split("");
let needZero = n - bArr.length;
for (let i = 0; i < needZero; i++) {
bArr.unshift("0");
}
return bArr.join("");
}
下面就是具体实现了:
function bPrint(f) {
let signBit; // 保存符号的字符串
let exponent; // 保存指数的字符串
let decimal; // 保存小数部分的字符串
// 首先求出负号位,就是看是正数还是负数
signBit = f > 0 ? "0" : "1";
// 求出指数部分
let i = 0; // 用于求出接近的指数
let fl = Math.abs(f); // 先前已经处理符号位了,所以我们直接操作正数
let increase = fl < 1 ? -1 : 1; // 看是不是小于 1 的数,是的话在求近似值的时候不一样
while (true) {
const pow2 = Math.pow(2, i);
if (increase === 1 && fl < pow2) {
// 如果是大于 1 的数,求小于
break;
} else if (increase === -1 && fl > pow2) {
//如果是小于 1 的数,求大于
break;
}
i += increase;
}
const k = i - (increase === 1 ? 1 : 0); // 保存的是要操作的数,这样下面就不用都判断了
exponent = zeroFill(uitoa(127 + k), 8);
// 小数部分
let radio = (f - Math.pow(2, k)) / (Math.pow(2, k + 1) - Math.pow(2, k));
decimal = zeroFill(uitoa(Math.round(radio * Math.pow(2, 23))), 23);
return signBit + exponent + decimal;
}
思路很简单,就是一部分一部分的求,规则上面都有,所以很简单,当然这也是我是码农的原因。同时也说明一件事情,只要把数学模型建好了一切都很简单。
看不懂代码的可以先看上面的原理,我就不过多解释了,不然就画蛇添足了。
我觉得我写代码永远都是按常规思路写,没有什么抽象可言。但我觉得我写的代码应该很容易懂吧,看到的人,如果认真看了,麻烦评价一下。
我想说明一下,测试验证是不是正确,可以使用 xcode ,只要加上断点在下面的 debug 窗口都能看到,默认显示的值是十进制,修改一下显示方式即可。记得创建 C 语言的程序,其他的我没看,但 C 语言的验证可以。