浮点数转二进制

6 投票
6 回答
34869 浏览
提问于 2025-04-16 10:51

我想把一个浮点数转换成二进制表示,应该怎么做呢?不过,我希望这个方法不仅限于二进制,最好能扩展到其他进制,比如三进制、四进制、八进制等等。

目前我已经有了一个简单的整数转换实现:

import string

LETTER = '0123456789' + string.ascii_lowercase
def convert_int(num, base):
    if base == 1:
        print "WARNING! ASKING FOR BASE = 1"
        return '1' * num if num != 0 else '0'

    if base > 36: raise ValueError('base must be >= 1 and <= 36')

    num, rest = divmod(num, base)
    rest = [LETTER[rest]]
    while num >= base:
        num, r = divmod(num, base)
        rest.append(LETTER[r])
    rest.reverse()
    return (LETTER[num] if num else '') + ''.join(str(x) for x in rest)

任何帮助都非常感谢 :)

补充:

def convert_float(num, base, digits=None):
    num = float(num)
    if digits is None: digits = 6

    num = int(round(num * pow(base, digits)))
    num = convert_int(num, base)
    num = num[:-digits] + '.' + num[:digits]
    if num.startswith('.'): num = '0' + num
    return num

这样做对吗?为什么我会得到这样的结果?

>>> convert_float(1289.2893, 16)
'509.5094a0'
>>> float.hex(1289.2983)
'0x1.42531758e2196p+10'

附言:如何将浮点数转换为二进制?

我看过那个讨论,但我还是没明白答案。我是说,这个方法只适用于0.25和0.125吗?还有我不理解“必须反向排列”这句话的意思……

6 个回答

10

如果你想把一个 float(浮点数)转换成一个字符串,并且希望小数点后有 d 位数字,可以按照以下步骤操作:

  1. 先把这个数字乘以 base**d(也就是基数的 d 次方)。
  2. 然后把结果四舍五入到最接近的整数。
  3. 接着把这个整数转换成字符串。
  4. 最后在字符串的末尾前面插入一个 .,并且在它前面留出 d 位数字。

举个例子,如果你想用12进制表示0.1,并且希望小数点后有4位数字,步骤如下:

  1. 0.1 × 124 = 2073.6
  2. 四舍五入到最接近的整数 → 2074
  3. 转换成字符串 → 124A
  4. 加上小数点 → 0.124A
18

对于浮点数,有一个内置的方法叫做 hex()。

http://docs.python.org/library/stdtypes.html#float.hex

这个方法可以给你一个数字的十六进制表示。把十六进制转换成二进制也很简单。

举个例子:

In [15]: float.hex(1.25)
Out[15]: '0x1.4000000000000p+0'

In [16]: float.hex(8.25)
Out[16]: '0x1.0800000000000p+3'
14

接下来我们来聊聊一些理论知识。

下面的解释并不涉及IEEE浮点标准,只是关于浮点数表示的一些基本概念

每个浮点数都是由一个小数部分、一个指数部分和一个符号组成的。还有一个叫做偏置的东西,用于处理指数部分,下面会详细解释。

所以我们有:

  1. 符号位
  2. 小数部分的数字
  3. 指数部分的数字

以基数2为例,使用8位小数和8位指数

小数部分的位数告诉我们,从下面这个序列中哪些数字需要被加到一起,来表示这个数的值:

2^-1 + 2^-2 + 2^-3 + 2^-4 + 2^-5 + 2^-6 + 2^-7 + 2^-8

比如说,如果小数部分是01101101,那么它的计算方式是:

0*2^-1 + 1*2^-2 + 1*2^-3 + 0*2^-4 + 1*2^-5 + 1*2^-6 + 0*2^-7 + 1*2^-8 = 0.42578125

现在,能够用这种方式表示的非零数字范围在 2 ** -8 = 0.00390625 和 1 - 2**-8 = 0.99609375之间。

这时候,指数部分就派上用场了。指数让我们可以通过将小数部分乘以指数,来表示非常大的数字。如果我们有一个8位的指数,就可以将结果的小数乘以0到2^255之间的数字。

回到上面的例子,假设指数是11000011 = 195。

我们的小数部分是01101101 = 0.42578125,指数部分是11000011 = 195。这样我们得到的数字是0.42578125 * 2^195,这个数字非常大。

到目前为止,我们可以表示的非零数字范围在2^-8 * 2^0和(1-2^-8) * 2^255之间。这让我们可以表示非常大的数字,但对于非常小的数字就不行了。为了能够表示小数字,我们需要在指数中加入所谓的偏置。偏置是一个总是从指数中减去的数字,以便能够表示小数字。

假设我们选择偏置为127。现在所有的指数都减去127。所以可以表示的数字范围在2^-8 * 2^(0 - 127)和(1-2^-8) * 2^(255 - 127 = 128)之间。

现在的例子数字是0.42578125 * 2^(195-127 = 68),这仍然是一个相当大的数字。

例子结束

为了更好地理解这些内容,可以尝试用不同的基数和小数、指数部分的大小进行实验。一开始不要用奇数基数,因为那样只会让事情变得复杂。

一旦你掌握了这种表示方法,你就应该能够写代码来获取任何数字在任何基数下的小数/指数部分组合的表示。

撰写回答