浮点数转二进制
我想把一个浮点数转换成二进制表示,应该怎么做呢?不过,我希望这个方法不仅限于二进制,最好能扩展到其他进制,比如三进制、四进制、八进制等等。
目前我已经有了一个简单的整数转换实现:
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 个回答
如果你想把一个 float
(浮点数)转换成一个字符串,并且希望小数点后有 d
位数字,可以按照以下步骤操作:
- 先把这个数字乘以
base**d
(也就是基数的d
次方)。 - 然后把结果四舍五入到最接近的整数。
- 接着把这个整数转换成字符串。
- 最后在字符串的末尾前面插入一个
.
,并且在它前面留出d
位数字。
举个例子,如果你想用12进制表示0.1,并且希望小数点后有4位数字,步骤如下:
- 0.1 × 124 = 2073.6
- 四舍五入到最接近的整数 → 2074
- 转换成字符串 →
124A
- 加上小数点 →
0.124A
对于浮点数,有一个内置的方法叫做 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'
接下来我们来聊聊一些理论知识。
下面的解释并不涉及IEEE浮点标准,只是关于浮点数表示的一些基本概念
每个浮点数都是由一个小数部分、一个指数部分和一个符号组成的。还有一个叫做偏置的东西,用于处理指数部分,下面会详细解释。
所以我们有:
- 符号位
- 小数部分的数字
- 指数部分的数字
以基数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),这仍然是一个相当大的数字。
例子结束
为了更好地理解这些内容,可以尝试用不同的基数和小数、指数部分的大小进行实验。一开始不要用奇数基数,因为那样只会让事情变得复杂。
一旦你掌握了这种表示方法,你就应该能够写代码来获取任何数字在任何基数下的小数/指数部分组合的表示。