难度中等
二进制数转字符串。给定一个介于0和1之间的实数(如0.72),类型为double,打印它的二进制表达式。如果该数字无法精确地用32位以内的二进制表示,则打印“ERROR”。
示例1:
输入:0.625 输出:"0.101"
示例2:
输入:0.1 输出:"ERROR" 提示:0.1无法被二进制准确表示
提示:
- 32位包括输出中的
"0."
这两位。 - 题目保证输入用例的小数位数最多只有
6
位
思路:一个很直观的想法就是一直做减法,如0.72,首先考虑0.5,则剩余0.32,接下来考虑0.18,依次类推,如果可以精确地用32位以内的二进制表示(即最小的位的权重为2^(-32)),即表示考虑32次小数二进制的减法,如果能减完,则可以精确表示,否则不行。
现在我们来考虑上述算法的优化问题,首先注意到题目条件输入用例的小数位数最多只有 6
位,其实也可以得到在这种条件下得到的二进制位最多也只有6位。
证明:我们可以将输入看成是如下式子(因为除2等价于二进制下右移1位)
此时我们可以知道b与2互质,即b是奇数。
我们回到十进制下考虑这个问题,我们设这个数可以表示为
上面两种表示均表示同一个数,因此有
由于我们前面已经约束过b与2互质,因此必须要满足x-6<=0,否则b就不满足与2互质了,即有x<=6。
当然,还有一个更简单单的方法:
可以看到,到第7个二进制小数后,精度已经扩展到小数点后第七位了,有冗余的第七位就更不可能精确表示六位小数了,而小数每次除2精度一定会往后扩展一位。
另外为了避免浮点精度误差的问题,我们在这里采用乘法计算小数二进制的方式,设初始值为num(0<num<1):
1.num=num*2
2.如果num>=1,则num=num-1,并上一位二进制1;否则上一位二进制0
这样做的理由其实就是二进制下,数扩大两倍等价于二进制位发生左移。
class Solution {
public:
string printBin(double num) {
string str = "";
double esp = 1e-10;
for(int count = 5; count >= 0 && num > 0; -- count){
num = num * 2;
if(num >= 1){
num -= 1;
str = str + '1';
}else{
str = str + '0';
}
}
if(num - (int)num < esp){
return "0." + str;
}
return "ERROR";
}
};