Проблема с реализацией натурального логарифма (ln) и возведением в степень - PullRequest
3 голосов
/ 26 октября 2019

Я пытаюсь следовать теме «Эффективная реализация натурального логарифма (ln) и возведения в степень», чтобы иметь возможность реализовать функцию журнала без математического выражения. Описанный алгоритм хорошо работает для значений от 1 до 2 (нормализованные значения). Однако, если значения не нормализованы и я следую инструкциям по нормализации, я получаю неправильные значения.

Ссылка: Нажмите здесь

Если я следую коду сПримерное целочисленное значение 12510, я получаю следующие результаты:

y = 12510 (0x30DE), log2 = 13, делитель = 26, x = 481,1538

float ln(float y) {
    int log2;
    float divisor, x, result;

    log2 = msb((int)y); // See: https://stackoverflow.com/a/4970859/6630230
    divisor = (float)(1 << log2);
    x = y / divisor;    // normalized value between [1.0, 2.0]

    result = -1.7417939 + (2.8212026 + (-1.4699568 + (0.44717955 - 0.056570851 * x) * x) * x) * x;
    result += ((float)log2) * 0.69314718; // ln(2) = 0.69314718

    return result;
}

Ожидаемый результатдля x должно быть нормализованное значение 1

Заранее благодарен за любую помощь

1 Ответ

4 голосов
/ 26 октября 2019

Из любопытства я попытался воспроизвести:

#include <stdio.h>

int msb(unsigned int v) {
  unsigned int r = 0;
  while (v >>= 1) r++;
  return r;
}

float ln(float y)
{
    int log2;
    float divisor, x, result;

    log2 = msb((int)y); // See: https://stackoverflow.com/a/4970859/6630230
    printf("log2: %d\n", log2);
    divisor = (float)(1 << log2);
    printf("divisor: %f\n", divisor);
    x = y / divisor;    // normalized value between [1.0, 2.0]
    printf("x: %f\n", x);
    result = -1.7417939 + (2.8212026 + (-1.4699568 + (0.44717955 - 0.056570851 * x) * x) * x) * x;
    result += ((float)log2) * 0.69314718; // ln(2) = 0.69314718
    return result;
}

int main()
{
  printf("ln(12510): %f\n", ln(12510));
}

Вывод:

log2: 13
divisor: 8192.000000
x: 1.527100
ln(12510): 9.434252

Live Demo on coliru

Я только что попробовал это в своем карманном калькуляторе Windows 7 и получил:

9.434283603460956823997266847405

9,434283603460956823997266847405

Первые 5 цифр идентичны. - Остальное я бы рассматривал как вопросы округления, не зная, какой из них стал ближе.

Однако в вопросе есть опечатка (или ошибка):

y = 12510 (0x30DE), log2 = 13, делитель = 26, x = 481,1538

divisor = (float)(1 << log2); с log2 = 13 выходами 8192.

log2 << 1 приведет к 26.

Ради интереса я изменил строку на divisor = (float)(log2 << 1); и получил следующий вывод:

log2: 13
divisor: 26.000000
x: 481.153839
ln(12510): -2982522368.000000

Итак, это меня немного озадачило:

Представленный код кажется правильным, но OP, по-видимому, неправильно его интерпретировал (или напоминал).

...