# 力扣每日一题之二进 制数转字符串 # 力扣每日一题之二进 制数转字符串 ## 题目 二进制数转字符串。给定一个介于0和1之间的实数(如0.72),类型为`double`,打印它的二进制表达式。如果该数字无法精确地用32位以内的二进制表示,则打印“`ERROR`”。 **示例1:** > **输入**:0.625 > **输出**:"0.101" **示例2:** > 输入:0.1 > 输出:"`ERROR`" 提示:0.1无法被二进制准确表示 **提示:** - 32位包括输出中的 `"0."` 这两位。 - 题目保证输入用例的小数位数最多只有 `6` 位 ## 分析 - 题目大致要求是将小数位数≤6的**小数**转成**32位以内的二进制数**输出,如超出范围则输出`ERROR`。 - 注意提示中说到32位**包括输出中的 "0." 这两位**,也就是转成的二进制数"0."后面最多只能有32-2=30位。 - 题目给出的函数是 ```c char* printBin(double num){} ``` 可以看到返回类型是char*字符串数组,所以需要将二进制存放进字符串数组再返回。 ## 十进制转二进制的方法 ### 正整数转二进制 **重复除二取余直到商为0,然后倒序排列,高位补零** 例子: 正整数**22**转二进制为**00010110** - 22/2……商11……余0 - 11/2……商5……余1 - 5/2……商2……余1 - 2/2……商1……余0 - 1/2……商0……余1 **0 1 1 0 1**倒过来就是22的二进制:**10110** 用8位二进制表示为0001 0110 用16位二进制表示为0000 0000 0001 0110 以此类推 > 计算机一般是8 位 16位 32位 64 位 ### 负整数转二进制 **先是将对应的正整数转换成二进制后,对二进制取反,然后对结果再加一** > 就是补码,计算机中的有符号数是以补码来计算的 例子: - 要计算**-22**的二进制数 - 首先算出**22**的二进制数:**10110** - **取反**为01001 - **加一**为01010 ### 小数转二进制 **重复将小数乘2取出整数部分直到0,正序排列** 例子: 小数0.625转二进制为**0.101** - 0.625*2……整数1……小数0.25 - 0.25*2……整数0……小数0.5 - 0.5*2……整数1……小数0.0 1 0 1就是22的二进制:0.101 #### 0.1无法转成二进制原因 - 0.1*2……整数0……小数0.2 - 0.2*2……整数0……小数0.4 - 0.4*2……整数0……小数0.8 - 0.8*2……整数1……小数0.6 - 0.6*2……整数1……小数0.2 - 0.2*2……整数0……小数0.4 - …… 永远无法让小数部分为0,所以无法转二进制 ## 思路 ### 转二进制部分: 让目标数字不断乘2,取走整数部分,直到为0或超过30次(32-2)。 ### 保存二进制部分: 创建一个大小为33的字符串数组(用malloc申请33个连续地址),为什么是33而不是32,因为字符串是以'\0'结尾的,所以要多出一个位置来存放'\0'。 ## 代码 ```c char *printBin(double num) { int i, t; char *bin = (char *)malloc(sizeof(char) * 33); //创建字符串数组 sprintf(bin, "%s", "0."); //把"0."输入到字符串里 for (i = 2; num && i < 32; i++) { /*i从2开始是为了给字符串存放数据(二进制)的时候从"0."以后开始 判断结束的条件是这个数已经为0了或者是次数超过32-2=30次(即超过32位)了 注意这里为什么不用<=32,因为这里的i代表下标,下标从0开始而不是1开始,所以减1 */ t = (int)(num * 2); //记录乘2后的整数值 num = num * 2 - t; //乘2后减去整数值,即保留乘2后的小数部分 bin[i] = t + '0'; //将整数部分(0或1)转成字符存进字符串 } if (i < 32) //如果次数没超过30次(即可以转成二进制) { bin[i] = '\0'; //给字符串的末尾存上'\0' return bin; //返回字符串首地址 } else { return "ERROR"; //反之则返回"ERROR" } } ```