Количество знаков после запятой при делении числа 2**128-1 на 2

Как разделить, в python, число 2*128-1 или ему подобное на 2, несколько раз до получения ответа типа 1.ххххххххх Необходимо максимально точно отобразить все знаки после запятой Пробовал разные варианты float и to fixed decimal

Внесу небольшую правку если число будет представлять из себя разность степеней двойки , 2 ** 128 - 2 ** 11 - 2 ** 6-1


Ответы (2 шт):

Автор решения: MBo

Это не так уж сложно с использованием длинной арифметики, имеющейся в Python для целых чисел.

Пусть степень n=4. Тогда 2^4-1 = 1111 в бинарном представлении, а нужный результат 1.111 - опять же в бинарном. В десятичном виде дробная часть выглядит как 0.5 + 0.25 + 0.125 = 0.875, а если часть до точки пока убрать, то 500+250+125

Получается, что вам нужно взять число t=10**(n-1), начальное значение s=0, и в цикле делить t пополам и прибавлять к s,а в конце вывести s в виде f'1.{s}'

def ppp(n, minus_set):
    t = 10**(n-1)
    s = 0
    for i in reversed(range(n-1)):
        t //= 2
        if not i in minus_set:
            s += t
    return f'1.{s}'

print(ppp(6,{2,4}))
>>> 1.34375

Проверяем: (64-16-4-1)/32 = 1.34375

→ Ссылка
Автор решения: Stanislav Volodarskiy

decimal

Простая постановка задачи

Для точного представления дроби (2p - 1) / 2p-1 нужно ровно p десятичных разрядов. Выставляем нужную точность и получаем результат:

import decimal

p = int(input())
with decimal.localcontext() as c:
    c.prec = p
    print(decimal.Decimal(2 ** p - 1) / 2 ** (p - 1))
$ echo 10 | python dec.py
1.998046875

$ echo 128 | python dec.py
1.9999999999999999999999999999999999999941225282458885624601563173138887716109066722161395623924562414686079137027263641357421875

p разрядов точности достаточно, потому что (2p - 1)·5p-1 < 2p·5p-1 < 2p·5p = 10p. А для него как раз и нужно p разрядов.

Усложним немного

Решим задачу для произвольного числа n. int.bit_length вычисляет для целого положительного числа его длину в битах. Обозначим её за p. Тогда нам нужно вывести n / 2p-1, а требуемая точность равна p.

import decimal


def print_fraction(n):
    p = n.bit_length()
    with decimal.localcontext() as c:
        c.prec = p
        print(decimal.Decimal(n) / 2 ** (p - 1))


def parse_exp(w):
    return (-1 if w.startswith('-') else 1), abs(int(w))

    
print_fraction(sum(s * 2 ** p for s, p in map(parse_exp, input().split())))
$ echo 10 -0 | python dec.py
1.998046875

$ echo 10 -5 -0 | python dec.py
1.935546875

$ echo 10 -8 | python dec.py
1.5

$ echo 128 -0 | python dec.py
1.9999999999999999999999999999999999999941225282458885624601563173138887716109066722161395623924562414686079137027263641357421875

$ echo 128 -11 -6 -0 | python dec.py
1.9999999999999999999999999999999999875809021835625324783102984842469744138457983927028953352600382231685216538608074188232421875

Целые

Простая постановка задачи

Вместо деления 2p - 1 на 2p-1 умножим его на 5p-1. Получится целое число, которое ровно в 10p-1 раз больше нужного ответа. Целое превратим в строку, вставим точку после первого знака и готово:

p = int(input())
s = str((2 ** p - 1) * (5 ** (p - 1)))
print(f'{s[0]}.{s[1:]}')
$ echo 10 | python int.py
1.998046875

$ echo 128 | python int.py
1.9999999999999999999999999999999999999941225282458885624601563173138887716109066722161395623924562414686079137027263641357421875

Усложним немного

Требуемая точность p вычисляется так же как и в случае c decimal. Затем делается умножение на 5p-1, дальше всё аналогично:

def print_fraction(n):
    p = n.bit_length()
    s = str(n * (5 ** (p - 1)))
    print(f'{s[0]}.{s[1:]}')


def parse_exp(w):
    return (-1 if w.startswith('-') else 1), abs(int(w))

    
print_fraction(sum(s * 2 ** p for s, p in map(parse_exp, input().split())))
$ echo 10 -0 | python int.py
1.998046875

$ echo 10 -5 -0 | python int.py
1.935546875

$ echo 10 -8 | python int.py
1.500000000

$ echo 128 -0 | python int.py
1.9999999999999999999999999999999999999941225282458885624601563173138887716109066722161395623924562414686079137027263641357421875

$ echo 128 -11 -6 -0 | python int.py
1.9999999999999999999999999999999999875809021835625324783102984842469744138457983927028953352600382231685216538608074188232421875

P.S. Оба способа опираются на знание какой будет результат. В первом способе надо определить нужную точность. Во втором способе деление на степень двойки заменяется умножением на степень пятёрки. Эти приемчики не сработают, например, для (3p - 1) / 3p-1. А даже для таких дробей есть способ решить задачу без трюков, полностью автоматически. Но поля книги слишком узки.

→ Ссылка