技术宅的结界

 找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 1034|回复: 1
收起左侧

【Python】十进制小数转二进制小数

[复制链接]

56

主题

162

帖子

8964

积分

用户组: 超级版主

OS与VM研究学者

UID
1043
精华
30
威望
449 点
宅币
7005 个
贡献
749 次
宅之契约
0 份
在线时间
1723 小时
注册时间
2015-8-15
发表于 2022-9-28 12:28:17 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

x
本帖最后由 tangptr@126.com 于 2022-9-28 23:30 编辑

前言

本文的诞生是源于完成作业的时候搞出来的无聊东西。
计算的不是浮点数,而是定点数。

基本理论

整数的表达方式是从右至左,从10的0次方一直到10的N次方。
小数的表达方式就是从小数点从左至右,从10的-1次方一直到10的-N次方。

比方说十进制的小数1.5表达为二进制的小数就是1.1,因为1.5=20+2-1

设计

由于整数太符合直觉了,所以我这里设计的程序就只管0到1的开区间以内的有限小数了。
浮点数精准度太差,再加上本来就是要算定点数,因此就用整数的方法搞计算了。

参数

我给出了三个参数:Number, Precision, PrecedingZeroes=0
Number参数用于表示小数点后的数字。
Precision参数表示精确至小数点后几位。
PrecedingZeroes参数用于表示当小数点后的开头几位为0的时候,有多少个零在那。

流程

在小数点后的计算十分容易,直接除以2就好。但是要把小数部分直接视为整数就有绝对不是除以2的问题了。
举个例子,如果用户想要表达0.5,那他会在Number参数这里传5,如果把5和2做比较就明显是错误的。
正确的做法是在10的进制空间下以2的商作为除数。
说人话就是:以5的正整数次幂为界,进行四舍五入式的比较。
以0.5为例,Number是5,是符合>=5的,因此小数点后一位置位。剩下0,结束,故0.5对应二进制是0.1。
以0.4为例,Number是4,不符合>=5,因此小数点后一位复位。剩下4,继续,故0.4对应二进制是0.0...。(暂时不展开计算)
接下来是如何进位的问题。求小数的时候我们需要向右借位,但是这里是整数,因此只能向左借位了。
向左借位的方法是:被除数乘以10,除数乘以5。注意不论是否置位复位,进行下一次迭代时都必须借位。
继续以0.4为例,由于还有余数,需要进位,因此被除数变成了40,除数变成了25。因为40>=25,所以小数点后两位置位。剩下15,还需要继续,0.4对应二进制就是0.01...。(不展开了)
但是问题还没有完。
如果用户传进来的Number是25呢?要知道0.25对应的二进制应该是0.01,照这么算它会变成0.11111....。还有,如果传进来的Number是500呢?0.500本质是0.5,可500的本质不是5啊。而且还有个PrecedingZeroes参数没有考虑在内呢。
于是就存在了初始化除数的问题。不能把除数直接初始化为5,而是要根据情况来定。
解决方案就是初始化的时候,在5的基础上,乘以10的非负整数次幂来消除长度问题。
以0.25为例,在第一次迭代时它应该和0.50作比较,换句话说就是25应该和50作比较。也就是说因为25大于10的1次幂,除数需要在初始化的时候也乘以10的1次幂。
以0.05为例,在第一次迭代时它应该和0.5作比较,但这不意味着就是5和5做比较,因为0.05相比0.5有效位少了1位,因此除数也需要在初始化的时候乘以10的1次幂。
在这个基础上,进位的过程可以优化一下。注意,这里优化的目的不是为了时间意义的优化,而是让除数在迭代的过程中不要太大。
如果除数自身可以被2整除,那么就将除数减半,被除数不变。
至于Precision参数没啥好说的,纯粹是控制迭代次数的活。

代码

说完流程就可以贴代码了

def BinaryFromDecimal(Number,Precision,PrecedingZeroes=0):
    BinaryString=""
    Remainder=Number
    # Initialize the divisor
    Extension=len(str(Number))-1+PrecedingZeroes
    Divisor=5*(10**Extension)
    # Start iteration.
    Digit=0
    while Digit<Precision:
        # Compare dividend and divisor.
        if Remainder>=Divisor:
            BinaryString+="1"
            Remainder-=Divisor
        else:
            BinaryString+="0"
        # If there is no remainder, conversion is over.
        if Remainder==0:
            break
        # Advance divisor and remainder.
        if Divisor%2:
            # If divisor is not divisible by two,
            # advance divisor and remainder by 5 and 10 times.
            Remainder*=10
            Divisor*=5
        else:
            # If divisor is divisible by two,
            # advancement would be dividing divisor by 2.
            Divisor//=2
        # Increment the digit total.
        Digit+=1
    return BinaryString

总结

其实早就有实现好的库了,比方说fxpmathbinary-fractions啥的。
所以才说本文搞出来的是无聊玩意。

评分

参与人数 1威望 +5 宅币 +10 贡献 +10 收起 理由
0xAA55 + 5 + 10 + 10 有助于学习十进制到二进制的转换.

查看全部评分

回复

使用道具 举报

52

主题

278

帖子

8938

积分

用户组: 管理员

UID
77
精华
16
威望
237 点
宅币
7860 个
贡献
246 次
宅之契约
0 份
在线时间
226 小时
注册时间
2014-2-22
发表于 2022-10-6 21:51:03 | 显示全部楼层
原来PYTHON的代码是这样的。。。我实在受不了没有括号也有没有end xxx的代码。

本版积分规则

QQ|申请友链||Archiver|手机版|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2022-12-9 03:09 , Processed in 0.060504 second(s), 29 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表