Scala |
您所在的位置:网站首页 › 二进制转换8进制公式 › Scala |
一.引言
Scala 提供了 toBinaryString 的方法,使得 Int 数字可以直接转换为二进制数字,但是小数不支持,下面介绍下如何针对给定小数 (Double) 转换为二进制小数。 A.官方 API 引言中提到给定 int 数字,官方支持直接调用 toBinaryString 生成二进制字符串: val intNum = 100 println(s"$intNum 转化为二进制=> ${intNum.toBinaryString}") 100 转化为二进制=> 1100100B.Scala 实现 手动实现只需要将对应整数持续除以2,余数保留,随后用商继续除以2,继续保留余数,直到商为0,将上述余数逆序反转即得到对应十进制整数的二进制形式,举个例子,下面求 66 的二进制: 整数: 66 商: 33 余数: 0 公式: 66 / 2 = 33 ...... 0 整数: 33 商: 16 余数: 1 公式: 33 / 2 = 16 ...... 1 整数: 16 商: 8 余数: 0 公式: 16 / 2 = 8 ...... 0 整数: 8 商: 4 余数: 0 公式: 8 / 2 = 4 ...... 0 整数: 4 商: 2 余数: 0 公式: 4 / 2 = 2 ...... 0 整数: 2 商: 1 余数: 0 公式: 2 / 2 = 1 ...... 0 整数: 1 商: 0 余数: 1 公式: 1 / 2 = 0 ...... 1将余数逆序排出得到 => 1000010,用 API 验证一下: val intNum = 66 println(s"$intNum 转化为二进制=> ${intNum.toBinaryString}") 66 转化为二进制=> 1000010下面 Scala 实现一下,思路就仿照上面的示例,循环退出条件为商为0,每次循环结束都将上一轮结果的商即 quotient 作为下一次循环的输入即 integer,这里使用 / 或者 >>1 都可以达到除以2的效果,并把每轮得到的余数 remainder 添加到 StringBuilder 中,待循环结束后反转即可。 def integerToBin(num: Double): String = { var integer = num.toInt val binaryString = new StringBuilder() var quotient = 0 // 商 var remainder = 0 // 余数 while (integer != 0) { quotient = integer >> 1 // quotient = integer / 2 remainder = integer % 2 binaryString.append(remainder) println(s"整数: $integer 商: $quotient 余数: $remainder 公式: $integer / 2 = $quotient ...... $remainder") integer = quotient } binaryString.toString().reverse }C.过程分析 看了一些网上的示例,大家都是只讲解了一直除以2,迭代商保留余数即可,但是没有讲为什么这么做,下面简单分析下自己的思考。给定数字 num,其二进制表示为: 其中 αn α(n-1) α(n-2) ... α0 即为对应十进制数字的二进制字符,下面 num 第一次除以2: 如果 α0=0,则余数为0,说明 num 的二进制表示对应的 2^0 次方的因子为0,如果 α0=1,则余数为1,说明对应 2^0 的因子为1,以此类推,第 n 次除法得到的余数可以代表展开式 2^{n-1} 对应的系数即二进制表达式的对应数字。num 表达式一共累加到 2^n,所以共需要执行 n+1 次除法,对应的 num=66 介于 [2^6, 2^7) 即 [64, 128] 的区间,所以其最大表示 n=6,所以 7次除法即可得到其对应二进制表示。至于为什么反转,因为余数得到的顺序依次为 α0-α1-α2-...-αn,而二进制表示是从最大位开始即 αn-...-α2-α1,所以最后余数的结果需要 reverse 才能得到正确结果。 2.十进制小数转二进制A.Demo 展示 上面提到了整数转二进制,下面沿着相同的思路求小数转二进制的方法。与上面整数示例每次除以2相反,求小数的二进制需要每次对小数乘以2,得到的部分如果大于等于1,则保留1,并将新的数字减去1参与后续迭代,如果得到的部分等于0,则保留0,直接将该部分参与下一次迭代。 初始化 decimal: 0.59375 multi: 0.0 decimal: 0.1875 multi: 1.1875 >=1: true addNum: 1 decimal: 0.375 multi: 0.375 >=1: false addNum: 0 decimal: 0.75 multi: 0.75 >=1: false addNum: 0 decimal: 0.5 multi: 1.5 >=1: true addNum: 1 decimal: 0.0 multi: 1.0 >=1: true addNum: 1上述示例为求小数 0.59375 的二进制小数表示方法,第一次乘以2 得到 decimal = 1.1875,由于 1.1875 大于1,所以第一位保留1,将 1.1875-1=0.1875 作为下一次迭代的起始数字,依次类推,直到小数部分为0 结束迭代,将所有数字顺序展开即为小数的二进制表示。 B.Scala 实现 def decimalToBin(num: Double): String = { val binaryString = new StringBuilder() var decimal = num - num.toInt var multi = 0D println(s"初始化 decimal: $decimal multi: $multi") while (decimal != 0D) { multi = decimal * 2 val addNum = if (multi >= 1) { decimal = multi - 1 1 } else { decimal = multi 0 } binaryString.append(addNum) println(s"decimal: $decimal multi: $multi >=1: ${multi >= 1} addNum: $addNum") } binaryString.toString() }按照上面的思路,首先将小数成2,然后判断是否大于等于1,然后将对应 add_num 添加至 StringBuilder 中,循环结束条件为 小数为0,即 decimal == 0.0 时,最后直接输出 StringBuilder.toString 即可,注意这里不需要反转了。 C.过程分析 与上面一样,我们通过展开式进行分析,给定一个小数 num,其二进制可以表示为: 其中 αn α(n-1) α(n-2) ... α0 即为对应十进制数字的二进制字符,下面 num 第一次乘以2: 此时如果 α0 为1,则整个 num >=1 ,代表 num 对应二进制在 2^-1 处因子为1,此时 num -1 即得到剩余小数部分为: 继续乘以2,即可得到 α1 的值,以此类推,num 最小位次为 2^{-n},则需要乘以2n次即可得到最终结果。以 0.59375 为例,其表示为 10011,表示为: Double 可以看做是 Integer + Decimal,所以 Double 转二进制只需要把 Double 拆解分别调用上述方法即可: def doubleToBin(num: Double): String = { val integerBin = integerToBin(num) val decimalBin = decimalToBin(num) val binaryString = integerBin + "." + decimalBin println(s"Double: $num BinaryString: $binaryString") binaryString }上面示例了 66 和 0.59375,下面试一下 66.59375: val num = 66.59375 val binaryString = doubleToBin(num) Double: 66.59375 BinaryString: 1000010.10011 三.二进制转十进制 1.二进制整数转十进制上面已经给了十进制转二进制的公式,反过来就是二进制转十进制的公式: 给定 α0 - αn,只需要代入上述公式即可: def binToInteger(binaryString: String): Int = { val integerString = binaryString.split("\\.")(0) var num = 0D integerString.reverse.toArray.zipWithIndex.foreach(info => { val factor = info._1 if (factor.equals('1')) { val index = info._2.toDouble num += math.pow(2, index) } }) num.toInt }通过 zipWithIndex 为二进制每一位绑定所以,注意这里需要将原数组 reverse 反转一下,如果二进制对应位置为1就使用 math.pow(2, index) 类加对应的数字。 2.二进制小数转十进制上面的公式同样适用: 与上面代码类似,也是用 math.pow() 实现累加的功能,不过这里 zipWithIndex 后需要 +1 并转负,因为整数是从 2^0 开始,小数是从 2^{-1} 开始。 3.二进制 Double 转十进制将上述两个二进制转十进制方法结合即可得到二进制 Double 转十进制的方法: def binToDouble(binaryString: String): Double = { val integerDec = binToInteger(binaryString) val decimalDec = binToDecimal(binaryString) val doubleNum = integerDec + decimalDec println(s"BinaryString: $binaryString Double: $doubleNum") doubleNum }将二进制字符拆分,分别转换为整数和小数即可: val num = 66.59375 val binaryString = doubleToBin(num) val doubleNum = binToDouble(binaryString) println("BinTransResult:" + doubleNum.equals(num))验证一下 doubleToBin 以及 binToDouble,非常的奈斯: Double: 66.59375 BinaryString: 1000010.10011 BinaryString: 1000010.10011 Double: 66.59375 BinTransResult:true 四.总结最后再随机生成10个 Double 验证一下: (0 to 10).foreach(epoch => { val num = math.random + math.random * 10 println(num) val binaryString = doubleToBin(num) val doubleNum = binToDouble(binaryString) println("BinTransResult:" + doubleNum.equals(num)) })二进制和十进制的转换其实只要写出展开式,不管整数还是小数都很好理解,除了二进制,同样适用于8 进制,16 进制。后续分析下 Scala Float 和 Double 浮点数使用的 IEEE754 标准浮点数转换,对浮点数和二进制做进一步熟悉。 |
今日新闻 |
点击排行 |
|
推荐新闻 |
图片新闻 |
|
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭 |