Есть ли способ остановить Джулию, используя научную (экспоненциальную) нотацию с BigFloats?

Хорошо, у меня есть, на первый взгляд, простая проблема. Я хочу взять десятичную часть иррационального числа до указанного числа цифр и рассматривать это как целое число. Например, если мое иррациональное число составляет 2,657829... и я хочу пять цифр, я ищу 65782 (хотя на самом деле я имею дело с "большими" числами).

Это легко сделать с помощью строк, например, если я хотел, чтобы десятичная часть корня от 3 до 50 цифр:

function main_1(n::Int, m::Int)::BigInt
    setprecision(Int(trunc((m + 3) * log2(10))))
    sr = string(sqrt(big(n)))
    ff = findfirst(sr, '.')
    dp = parse(BigInt, sr[ff + 1:ff + m])
    return dp
end

@time main_1(3, 50)

Выход 73205080756887729352744634150587236694280525381038,

Однако я негодую на использование строк, когда имею дело только с числами! То, что я хочу сделать, это начать с BigFloat, вычесть целую часть, умножить результат на соответствующий коэффициент 10, округлить результат до нуля, а затем преобразовать его в BigInt. Проблема в том, что Джулия использует научную / экспоненциальную нотацию, поэтому я не могу достичь того, чего хочу, используя только цифры. Следующий (частичный) код показывает проблему:

function main_2(n::Int, m::Int)::BigFloat
    setprecision(Int(trunc((m + 3) * log2(10))))
    sr = sqrt(big(n))
    tr = trunc(sr)
    dp = (sr - tr) * big(10.0) ^ 50
    return dp
end

@time main_2(3, 50)

Выход в этом случае 7.32050807568877293527446341505872366942805253810380625e+49 (есть несколько дополнительных цифр, которые были бы удалены на этапе округления).

Итак, мой вопрос, есть ли способ достичь моей цели, не прибегая к последовательности?

1 ответ

Решение

Один из способов добиться этого без использования строк - преобразовать результат и его целочисленную часть в BigInt перед тем, как выполнить вычитание (и изменить тип функции с BigFloat на BigInt):

function main_2(n::Int, m::Int)::BigInt
    setprecision(Int(trunc((m + 3) * log2(10))))

    # Calc the sqrt
    result = sqrt(big(n))

    # Convert the whole number to BigInt to the specified precision
    sr = convert(BigInt, trunc(result*big(10)^m))

    # Convert the integer part to BigInt
    tr = convert(BigInt, trunc(result)*big(10)^m)

    dp = sr - tr
    return dp
end

Сравнивая приведенную выше реализацию с main_1 функция, есть небольшое улучшение:

julia> @time main_1(3, 50)
  0.000042 seconds (36 allocations: 5.254 KiB)
73205080756887729352744634150587236694280525381038

julia> @time main_2(3, 50)
  0.000028 seconds (51 allocations: 1.617 KiB)
73205080756887729352744634150587236694280525381038

Редактировать:

Другой способ (как прокомментировал @Bill) - просто усечь результат (чтобы избавиться от InexactError()) и измените тип функции на BigInt:

function main_2(n::Int, m::Int)::BigInt
    setprecision(Int(trunc((m + 3) * log2(10))))
    sr = sqrt(big(n))
    tr = trunc(sr)
    dp = (sr - tr) * big(10.0) ^ 50
    return trunc(dp)
end

После тестирования:

julia> @time main_2(3,50)
  0.000026 seconds (28 allocations: 1.016 KiB)
73205080756887729352744634150587236694280525381038
Другие вопросы по тегам