Подтвердить что ты не робот

Pow(), кажется, отсутствует здесь

Что здесь происходит:

#include <stdio.h>
#include <math.h>
int main(void) {
    printf("17^12 = %lf\n", pow(17, 12));
    printf("17^13 = %lf\n", pow(17, 13));
    printf("17^14 = %lf\n", pow(17, 14));
}

Я получаю этот вывод:

17^12 = 582622237229761.000000
17^13 = 9904578032905936.000000
17^14 = 168377826559400928.000000

13 и 14 не соответствуют wolfram alpa cf:

12: 582622237229761.000000
    582622237229761

13: 9904578032905936.000000
    9904578032905937

14: 168377826559400928.000000
    168377826559400929

Кроме того, он не ошибается какой-то странной фракцией - он ошибочен ровно одним!

Если это до меня доходит до предела того, что pow() может сделать для меня, есть ли альтернатива, которая может рассчитать это? Мне нужна функция, которая может вычислять x^y, где x^y всегда меньше ULLONG_MAX.

4b9b3361

Ответ 1

pow работает с номерами double. Они представляют собой числа вида s * 2 ^ e, где s - 53-битное целое число. Поэтому double может хранить все целые числа ниже 2 ^ 53, но только некоторые целые числа выше 2 ^ 53. В частности, он может представлять только четные числa > 2 ^ 53, так как при е > 0 значение всегда кратно 2.

17 ^ 13 требуется 54 бит для представления точно, поэтому e устанавливается в 1 и, следовательно, вычисленное значение становится четным числом. Правильное значение нечетное, поэтому это не удивительно. Аналогично, 17 ^ 14 принимает 58 бит для представления. То, что он тоже отключен одним, - удачное совпадение (если вы не применяете слишком много теории чисел), это просто один из кратных 32, который является гранулярностью, при которой double числа эта величина округлена.

Для точного целочисленного возведения в степень вам следует использовать целые числа. Напишите свою собственную процедуру double -free exponentiation. Используйте возведение в степень возведением в квадрат, если y может быть большим, но я предполагаю, что это всегда меньше 64, что делает эту проблему спорное.

Ответ 2

Числа, которые вы получаете, слишком велики, чтобы их можно было точно представить с помощью double. Число с плавающей запятой с двойной точностью имеет по существу 53 значащих двоичных разряда и может представлять все целые числа до 2^53 или 9,007,199,254,740,992.

Для более высоких чисел последние цифры усекаются, а результат вычисления округляется до следующего числа, которое может быть представлено как double. Для 17^13, который немного выше предела, это самое близкое четное число. Для чисел больше 2^54 это самое близкое число, которое делится на четыре и т.д.

Ответ 3

Если ваши входные аргументы являются целыми неотрицательными значениями, вы можете реализовать свой собственный pow.

Рекурсивный:

unsigned long long pow(unsigned long long x,unsigned int y)
{
    if (y == 0)
        return 1;
    if (y == 1)
        return x;
    return pow(x,y/2)*pow(x,y-y/2);
}

Итеративно:

unsigned long long pow(unsigned long long x,unsigned int y)
{
    unsigned long long res = 1;
    while (y--)
        res *= x;
    return res;
}

Эффективно:

unsigned long long pow(unsigned long long x,unsigned int y)
{
    unsigned long long res = 1;
    while (y > 0)
    {
        if (y & 1)
            res *= x;
        y >>= 1;
        x *= x;
    }
    return res;
}

Ответ 4

Небольшое дополнение к другим хорошим ответам: в архитектуре x86 обычно имеется x87 80-разрядный расширенный формат, который поддерживается большинством C компиляторов с помощью long double. Этот формат позволяет работать с целыми числами до 2^64 без пробелов.

Существует аналог pow() в <math.h>, который предназначен для работы с номерами long double - powl(). Следует также заметить, что спецификатор формата для значений long double отличается от double единиц - %Lf. Поэтому правильная программа, использующая тип long double, выглядит следующим образом:

#include <stdio.h>
#include <math.h>
int main(void) {
    printf("17^12 = %Lf\n", powl(17, 12));
    printf("17^13 = %Lf\n", powl(17, 13));
    printf("17^14 = %Lf\n", powl(17, 14));
}

Как отметил Стивен Канон в комментариях, нет гарантии, что эта программа должна дать точный результат.