在Python中将大(十进制模块)浮点数转换为二进制表示?

时间:2014-08-14 16:54:20

标签: python math

所以,我试图从python decimal模块定义的十进制数中得到一长串的位,目前就是这个:

Decimal('3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303820')

(顺便说一下,只需pi到200位。)

我想要二进制表示中的所有内容,因此我可以将其转换为十六进制数字数组,表示3以下的所有内容。

关于如何做到这一点的任何想法?我一直在查看模块,但它似乎只提供严格的基于十进制的函数。

2 个答案:

答案 0 :(得分:2)

对于这个问题,我深表歉意,但对于仍然需要对此问题(使用十进制)的任何人来说,可以编写自己的转换方法。它的效率不会很高,但如果您在生成二进制数字时暂时提高精度,它可以足够准确。任何基数中的数字(大于 1)只是给定基数的幂的系数。下面的注释代码概述了将任何数字从十进制转换为任何其他基数的整个过程(当然,如果新基数大于 1)。该算法适用于任何可以支持对数和浮点数的任意精度 python 模块。

from decimal import *

def convert_base(x, new_base, preferred_digits=200):
    # ensure that x and new_base both have arbitrary precision
    x = Decimal(x)
    new_base = Decimal(new_base)

    # compute the log floor of x -- this is the exponent of the power of new_base of which the leading digit of x (in base new_base) is the coefficient
    # for example: pi in base 10 is 3.14159... but in base 2 it's 1*2 + 1*1 + 0*0.5 + ... which means exponent=1 because that sum starts with 2 raised to the power of exponent=1 (1*2^1 + ...)
    # further, log(pi) = 1.65... which means the floor is floor(log(pi)) = 1, so again, p=1.
    # in other words, exponent represents the largest exponent on the power of new_base where the power of new_base is less than or equal to the given x.
    exponent = (x.log10()/Decimal(new_base).log10()).__floor__()

    # we need a copy of this value to return it
    largest_exponent = exponent

    # start at the value of x
    # we will work our way down to 0, or get very close to it
    # ideally, we'll come up with a sufficient number of digits
    v = x
    
    # the list of digits that this function will return after populating it
    digits = []

    # keep decreasing v until it's extremely close to 0 (such that it becomes 0 with the provided precision) or the number of digits we generate reaches or exceeds our preferred_digits
    while v > 0 and len(digits) < preferred_digits:
        # the current power of new_base
        power = new_base ** exponent

        # compute the coefficient of the power (this is the next digit to add to digits)
        # we want this to be an integer, so we'll floor it
        coefficient = int(v / power)
        digits.append(coefficient)

        # since v is a sum of powers of new_base, we can now subtract power * coefficient from v, and we'll be left with the exact same problem as before, but we have obtained one digit
        # so we just rinse and repeat until we have enough digits, or all that there are in the converted representation
        v -= power * coefficient

        # to continue the process, we must make sure that the power of new_base is decreasing (so we increment p, the exponent of new_base, as power has -p as the exponent)
        # the whole point of using -p as the exponent to find the power of the new_base is to use multiplication in this loop instead of division (so it's slightly faster)
        exponent -= 1

    # we return digits, but also the largest exponent so we know where to put the "point" in the number
    # every number in any base is represented by a mantissa and an exponent.  here, largest_exponent is the exponent of the number's representation in base new_base
    # digits are part of the mantissa, so we've essentially converted x into base new_base
    return digits, largest_exponent


# we can also convert back to decimal from any base
# we just sum the products of the digits and the corresponding powers of from_base, based on exponent
def convert_to_decimal(digits, exponent, from_base):
    # again, from_base can have arbitrary precision
    from_base = Decimal(from_base)

    # start with a zero sum
    # this sum will yield our original x that we passed to convert_base()
    x = 0

    # create a copy of the exponent, but we do this so we don't have to modify the argument of the function itself (just a good practice)
    p = exponent

    # now we iterate over the digits, decreasing exponent (p) each time
    for d in digits:
        # compute the power
        power = from_base ** p

        # multiply by coefficient (current digit)
        product = power * d

        # add to the total sum
        x += product

        # decrease the exponent (p)
        p -= 1

    # the sum is now a decimal representation of number that has the given mantissa (digits) and exponent (exponent) in the given base (from_base)
    return x




digits, exponent = convert_base(Decimal('3.1415926535'), 2)

print(digits)
print(exponent)

reconverted = convert_to_decimal(digits, exponent, 2)

print(reconverted)

# some rounding errors of course, but that's nearly impossible to avoid
# one simple workaround is to temporarily increase the digits of precision by 2 or 3 digits for the conversion processes

好的,我已经从技术上回答了这个问题。但我真的想强调的是,Python 的十进制模块并不是实现任意精度浮点数的最佳方式。我强烈推荐mpmath。我已经在无数个人项目中使用过它,它从来没有让我失望!这是使用 mpmath 的相同算法。

from mpmath import *

def convert_base(x, new_base, preferred_digits=200):
    x = mpf(x)
    new_base = mpf(new_base)
    exponent = int(floor(log(x)/log(new_base)))
    largest_exponent = exponent
    v = x
    digits = []
    while v > 0 and len(digits) < preferred_digits:
        power = new_base ** exponent
        coefficient = int(v / power)
        digits.append(coefficient)
        v -= power * coefficient
        exponent -= 1
    return digits, largest_exponent

def convert_to_decimal(digits, exponent, from_base):
    from_base = mpf(from_base)
    x = 0
    p = exponent
    for d in digits:
        power = from_base ** p
        product = power * d
        x += product
        p -= 1
    return x



digits, exponent = convert_base(mpf('3.1415926535'), 2)

print(digits)
print(exponent)

reconverted = convert_to_decimal(digits, exponent, 2)

print(reconverted)

# still some rounding errors with mpmath, but they're not displayed because it's smart

我们甚至可以比较执行时间。

import timeit

decimal_time = timeit.timeit('using_decimal.convert_base(3.1415926535, 2, 200)', number=10000, setup='import using_decimal')
mpmath_time = timeit.timeit('using_mpmath.convert_base(3.1415926535, 2, 200)', number=10000, setup='import using_mpmath')

print(f'decimal time: {decimal_time} seconds')
print(f'mpmath time: {mpmath_time} seconds')
print('Speed improvement: {:+.1f}%'.format((decimal_time-mpmath_time)*100./mpmath_time))
decimal time: 4.2381332 seconds
mpmath time: 3.4054762 seconds
Speed improvement: +24.5%

希望这会有所帮助!

答案 1 :(得分:0)

可以使用gmpy2完成。

>>> import gmpy2
>>> pi=gmpy2.const_pi(precision=100)
>>> "{0:A}".format(pi)
'0X3.243F6A8885A308D313198A2EP+0'
>>> 

使用help(gmpy2.mpfr().__format__)了解mpfr类型支持的格式选项的详细信息。

免责声明:我维持gmpy2。