Scala

您所在的位置:网站首页 二进制转换8进制公式 Scala

Scala

2024-07-14 13:50:58| 来源: 网络整理| 查看: 265

一.引言

Scala 提供了 toBinaryString 的方法,使得 Int 数字可以直接转换为二进制数字,但是小数不支持,下面介绍下如何针对给定小数 (Double) 转换为二进制小数。

二.十进制转二进制 1.十进制整数转二进制

A.官方 API

引言中提到给定 int 数字,官方支持直接调用 toBinaryString 生成二进制字符串:

val intNum = 100 println(s"$intNum 转化为二进制=> ${intNum.toBinaryString}") 100 转化为二进制=> 1100100

B.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,其二进制表示为:

num = \alpha _0{2^0} + \alpha _1{2^1} + ... + \alpha _n{2^n}

其中 αn α(n-1) α(n-2) ... α0 即为对应十进制数字的二进制字符,下面 num 第一次除以2:

quotient = \alpha _1{2^0} + ... + \alpha _n{2^{n-1}}

remainder=\alpha _0

如果 α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,其二进制可以表示为:

num = \alpha _0 2^{-1} + \alpha _12^{-2} + ... + \alpha 2^{-n}

其中 αn α(n-1) α(n-2) ... α0 即为对应十进制数字的二进制字符,下面 num 第一次乘以2:

num = \alpha _0 + \alpha _12^{-1} + ... + \alpha 2^{-(n-1)}

此时如果 α0 为1,则整个 num >=1 ,代表 num 对应二进制在 2^-1 处因子为1,此时 num -1 即得到剩余小数部分为:

decimal = \alpha _12^{-1} + ... + \alpha 2^{-(n-1)}

继续乘以2,即可得到 α1 的值,以此类推,num 最小位次为 2^{-n},则需要乘以2n次即可得到最终结果。以 0.59375 为例,其表示为 10011,表示为:

num = 1* 2^{-1} + 1*2^{-4} + ... + 1* 2^{-5}

3.十进制 Double 转二进制

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.二进制整数转十进制

上面已经给了十进制转二进制的公式,反过来就是二进制转十进制的公式:

num = \alpha _0{2^0} + \alpha _1{2^1} + ... + \alpha _n{2^n}

给定 α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.二进制小数转十进制

上面的公式同样适用:

num = \alpha _0 2^{-1} + \alpha _12^{-2} + ... + \alpha 2^{-n}

def binToDecimal(binaryString: String): Double = { val decimalString = binaryString.split("\\.")(1) var num = 0D decimalString.toArray.zipWithIndex.foreach(info => { val factor = info._1 if (factor.equals('1')) { val index = -1 * (info._2.toDouble + 1) num += math.pow(2, index) } }) num }

 与上面代码类似,也是用 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 标准浮点数转换,对浮点数和二进制做进一步熟悉。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭