LINUX.ORG.RU

десятичный логарифм элементарными действиями

 логарифм,


1

1

Как вычислить десятичный логарифм при помощи элементарных арифметических операций? Не важно на каком языке. Может даже через вычисление логарифмов по другим основаниям...

P.S. Готовые функции не предлагать, просто в Verilog нет готовых функций на эту тему, но есть конечно готовые громоздкие IP-core. Мне же надо это не для синтеза схемы, а просто для тестбенча, где доступен императивный подход.

По таблице + разложение в ряд Тейлора

buddhist ★★★★★
()

Может даже через вычисление логарифмов по другим основаниям...

Я не знаю верилог, но lg(x) = ln(x)/ln(10).

ya-betmen ★★★★★
()
Ответ на: комментарий от anonymous

Еще лучше использовать не ln(1+x), а следующее:
ln((1+x)/(1-x)) = ln(1+x) - ln(1-x)
Дело в том, что ln(1+x) плохо сходится при x=1 (и близких значениях). Однако в предложенном варианте ln(2) находится при x=1/3.

iVS ★★★★★
()

http://melpon.org/wandbox/permlink/ZIzQgnd9Q3wyy9eM самое банальное. Умножение на 10 делается через сдвиги и сложение http://goo.gl/K8Fs6u

Если надо быстро, то таблицей поиска можно. Например если десятичный логарифм по основанию 10 от числа в диапазоне от 0 до 9999 то делаем сравнение через https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html
от 0 до 9 - 0;
от 10 до 99 - 1;
итд

SZT ★★★★★
()

Забавно читать решения с использованием вещественной арифметики для Verilog, когда не указано вообще, с какой точностью надо этот логарифм считать и можно(рационально) ли для этого использовать плавучку. Если нужен десятичный логарифм чтоб со знаками после запятой, можно описанный тут способ http://everything2.com/title/Logarithm algorithm попробовать

SZT ★★★★★
()
Ответ на: комментарий от SZT

Готовые функции не предлагать, просто в Verilog нет готовых функций на эту тему, но есть конечно готовые громоздкие IP-core. Мне же надо это не для синтеза схемы, а просто для тестбенча, где доступен императивный подход.

Особой точности не надо, хотя бы пару знаков после запятой.

I-Love-Microsoft ★★★★★
() автор топика
Ответ на: комментарий от I-Love-Microsoft

Ну тут все достаточно просто, надо только знать свойства логарифма. Например если надо найти с точностью до 2 знаков логарифм
log10 (123456) можно сделать следующие шаги:
log10 (123456) (используя код http://melpon.org/wandbox/permlink/ZIzQgnd9Q3wyy9eM ) = 5 (с точностью до целых, нет дробной части) 10 ^ 5 = 100000
Сделам log10 который определен на интервале от 1 до 9.9999999999... через таблицу поиска (типа https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html ) c промежутками, которых достаточно для получения нужной точности log10 (123456/100000) = 0.0915122...
http://www.wolframalpha.com/input/?i=log10 (123456/100000)
Примерное решение (с двумя знаками): 5 + 0.09 = 5.09
http://www.wolframalpha.com/input/?i=log10 (123456) и действительно, так и есть

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 2)
Ответ на: комментарий от iVS

Да, я уже реализовал и вижу - при x близким к 1 вообще ужасный результат. Но я не понял, как из этого ln((1+x)/(1-x)) = ln(1+x) - ln(1-x) высчитать тупо ln(x)???

log10(x) = ln(x)/ln(10) я уже сделал и это работает точно.

I-Love-Microsoft ★★★★★
() автор топика
Ответ на: комментарий от I-Love-Microsoft

Но я не понял, как из этого ln((1+x)/(1-x)) = ln(1+x) - ln(1-x) >>высчитать тупо ln(x)???

Если нужно вычислить ln a, то, очевидно, (1+x)/(1-x) = a. Откуда x = (a-1)/(a+1).

zhekas
()
Ответ на: комментарий от zhekas

Спасибо, Евгений Перельман :) Эх, совсем я заржавел за 8 лет после универа...

I-Love-Microsoft ★★★★★
() автор топика
Ответ на: комментарий от Confucij

Этот код дает неверный результат:

  int fxlog(int x) {
  int t,y;

  y=0xa65af;
  if(x<0x00008000) x<<=16,              y-=0xb1721;
  if(x<0x00800000) x<<= 8,              y-=0x58b91;
  if(x<0x08000000) x<<= 4,              y-=0x2c5c8;
  if(x<0x20000000) x<<= 2,              y-=0x162e4;
  if(x<0x40000000) x<<= 1,              y-=0x0b172;
  t=x+(x>>1); if((t&0x80000000)==0) x=t,y-=0x067cd;
  t=x+(x>>2); if((t&0x80000000)==0) x=t,y-=0x03920;
  t=x+(x>>3); if((t&0x80000000)==0) x=t,y-=0x01e27;
  t=x+(x>>4); if((t&0x80000000)==0) x=t,y-=0x00f85;
  t=x+(x>>5); if((t&0x80000000)==0) x=t,y-=0x007e1;
  t=x+(x>>6); if((t&0x80000000)==0) x=t,y-=0x003f8;
  t=x+(x>>7); if((t&0x80000000)==0) x=t,y-=0x001fe;
  x=0x80000000-x;
  y-=x>>15;
  return y;
  }

I-Love-Microsoft ★★★★★
() автор топика
Ответ на: комментарий от I-Love-Microsoft

Но я не понял, как из этого ln((1+x)/(1-x)) = ln(1+x) - ln(1-x) высчитать тупо ln(x)???

(1+t)/(1-t)=x => t=(x-1)/(x+1)
ln(x)=ln(1+t) - ln(1-t)
Учти еще, что хотя разложение ln(1+t)=t-(t^2)/2+(t^3)/3+... содержит все возможные степени t, при вычислении разности
ln(1+t) - ln(1-t)=2(t + (t^3)/3 + ...)
четные степени уходят. Кстати, здесь ответ, почему данный ряд сходится лучше исходного: из знакопеременного мы перешли к ряду знакопостоянному.

iVS ★★★★★
()
Последнее исправление: iVS (всего исправлений: 1)
Ответ на: комментарий от iVS

Вот что получилось, алгоритм неверно считает, мне нужно считать логарифмы чисел порядка от 100 до 500000:

#include <QCoreApplication>
#include <QDebug>

qreal my_ln(qreal x);
qreal my_lnb(qreal x);
qreal my_log(qreal x);
int main(int argc, char *argv[])
{
	QCoreApplication a(argc, argv);
	qDebug("ln= %.10f", my_ln((qreal)1.5)); // OK
	qDebug("log= %.10f", my_log((qreal)1.5)); // OK
	qDebug("lnb= %.10f", my_lnb((qreal)1.5)); // OK
	qDebug("lnb(1450)v1= %.10f", -my_lnb((qreal)1/1450)); // failed, must be 7.279318835
	qDebug("lnb(1450)v2= %.10f", my_lnb((qreal)1450)); // failed, must be 7.279318835
	return 0;
}

qreal my_log(qreal x)
{
	return (qreal) my_ln(x) / 2.302585093;
}

qreal my_lnb(qreal x)
{
	qreal t = (qreal) (1 - x) / (1 + x);
	qreal r = -(my_ln(1 + t) - my_ln(1 - t));
	return r;
}

qreal my_ln(qreal x)
{
	qreal r = (x - 1);
	bool sign = false;
	for(int i = 2; i < 20; i++)
	{
		qreal px = (x - 1);
		for(int j = 0; j < (i - 1); j++) px *= (x - 1);
		if(sign) r += (qreal) px / i;
		else r -= (qreal) px / i;
		sign = !sign;
	}
	return r;
}

I-Love-Microsoft ★★★★★
() автор топика
Ответ на: комментарий от Confucij

Учел, но даже если поделить на 2^16 получится странный результат. Причем пока я не добавил unsigned то получался вообще результат со знаком минус:

unsigned int fxlog(int x)
{
	unsigned int t = 0, y = 0;
	y=0xa65af;
	if(x<0x00008000) x<<=16,              y-=0xb1721;
	if(x<0x00800000) x<<= 8,              y-=0x58b91;
	if(x<0x08000000) x<<= 4,              y-=0x2c5c8;
	if(x<0x20000000) x<<= 2,              y-=0x162e4;
	if(x<0x40000000) x<<= 1,              y-=0x0b172;
	t=x+(x>>1); if((t&0x80000000)==0) x=t,y-=0x067cd;
	t=x+(x>>2); if((t&0x80000000)==0) x=t,y-=0x03920;
	t=x+(x>>3); if((t&0x80000000)==0) x=t,y-=0x01e27;
	t=x+(x>>4); if((t&0x80000000)==0) x=t,y-=0x00f85;
	t=x+(x>>5); if((t&0x80000000)==0) x=t,y-=0x007e1;
	t=x+(x>>6); if((t&0x80000000)==0) x=t,y-=0x003f8;
	t=x+(x>>7); if((t&0x80000000)==0) x=t,y-=0x001fe;
	x=0x80000000-x;
	y-=x>>15;
	return y;
}

qreal new_ln(qreal x)
{
	unsigned int li = fxlog(x);
	qDebug() << "for" << x << "li=" << li;
	return 0;
}
for 1450 li= 4294717537

I-Love-Microsoft ★★★★★
() автор топика
Ответ на: комментарий от I-Love-Microsoft
#include <stdio.h>

unsigned int fxlog(int x)
{
	unsigned int t = 0, y = 0;
	y=0xa65af;
	if(x<0x00008000) x<<=16,              y-=0xb1721;
	if(x<0x00800000) x<<= 8,              y-=0x58b91;
	if(x<0x08000000) x<<= 4,              y-=0x2c5c8;
	if(x<0x20000000) x<<= 2,              y-=0x162e4;
	if(x<0x40000000) x<<= 1,              y-=0x0b172;
	t=x+(x>>1); if((t&0x80000000)==0) x=t,y-=0x067cd;
	t=x+(x>>2); if((t&0x80000000)==0) x=t,y-=0x03920;
	t=x+(x>>3); if((t&0x80000000)==0) x=t,y-=0x01e27;
	t=x+(x>>4); if((t&0x80000000)==0) x=t,y-=0x00f85;
	t=x+(x>>5); if((t&0x80000000)==0) x=t,y-=0x007e1;
	t=x+(x>>6); if((t&0x80000000)==0) x=t,y-=0x003f8;
	t=x+(x>>7); if((t&0x80000000)==0) x=t,y-=0x001fe;
	x=0x80000000-x;
	y-=x>>15;
	return y;
}

int main() {
    
    unsigned int log_result = fxlog(10 << 16);
    printf("Log result in fixed:%u result in float: %f\n", log_result, (float)log_result / 65536);

    return 0;
}
sh-4.3$ 
Log result in fixed:150902 result in float: 2.302582 
Confucij
()
Ответ на: комментарий от Confucij

Сунул в исходник с расширением *.c и стал корректный результат!!! Годный точный до третьего-четвертого знака после запятой результат! Спасибо! Правда максимальное входное число это 32767, но я уже дальше свойствами логарифмов добью до нужного диапазона...

Спасибо всем за проявленное сострадание :)

I-Love-Microsoft ★★★★★
() автор топика
Ответ на: комментарий от I-Love-Microsoft

Большие числа нужно преобразовывать к маленьким. В частности 1450 = 1000*1,45 Тогда lg 1450 = 3+lg 1,45

zhekas
()
Ответ на: комментарий от I-Love-Microsoft

Да не за что. Мне только вот что интересно - как ты его будешь к симулятору прикручивать? Есть возможность мешать Verilog и C в тестбенче или просто в лоб переписать на Verilog?

Confucij
()
Ответ на: комментарий от Confucij

Есть возможность мешать Verilog и C в тестбенче или просто в лоб переписать на Verilog?

Я уже переписал на Verilog для тестбенча, всё работает как часики. А уже отдельная функция домножает на коэффициент чтобы логарифм стал десятичным и умноженным на 20.

Завтра чуть причешу и выложу исходник, да там всё на 99% идентично версии на языке Си.

Есть конечно PLI и да, можно тупо совать модули на Си в код на Verilog но лишь для моделей в симуляторах, я это делал но наверное лет 8 назад, уже забыл как, а сейчас мне проще на Verilog вкатить.

I-Love-Microsoft ★★★★★
() автор топика
Последнее исправление: I-Love-Microsoft (всего исправлений: 1)
Ответ на: комментарий от Confucij
function int fxlog(int x);
	longint y = 32'hA65AF, t = 0;
	if(x < 32'h00008000) begin x <<= 16; y -= 32'hb1721; end
	if(x < 32'h00800000) begin x <<= 8; y -= 32'h58b91; end
	if(x < 32'h08000000) begin x <<= 4; y -= 32'h2c5c8; end
	if(x < 32'h20000000) begin x <<= 2; y -= 32'h162e4; end
	if(x < 32'h40000000) begin x <<= 1; y -= 32'h0b172; end
	t = x + (x>>1); if((t & 32'h80000000) == 0) begin x = t; y -= 32'h067cd; end
	t = x + (x>>2); if((t & 32'h80000000) == 0) begin x = t; y -= 32'h03920; end
	t = x + (x>>3); if((t & 32'h80000000) == 0) begin x = t; y -= 32'h01e27; end
	t = x + (x>>4); if((t & 32'h80000000) == 0) begin x = t; y -= 32'h00f85; end
	t = x + (x>>5); if((t & 32'h80000000) == 0) begin x = t; y -= 32'h007e1; end
	t = x + (x>>6); if((t & 32'h80000000) == 0) begin x = t; y -= 32'h003f8; end
	t = x + (x>>7); if((t & 32'h80000000) == 0) begin x = t; y -= 32'h001fe; end
	x = 32'h80000000 - x;
	y -= x>>15;
	fxlog = y;
endfunction
I-Love-Microsoft ★★★★★
() автор топика
Ответ на: комментарий от Confucij

Это вообще никак не будет синтезироваться, но такую вот функцию не сложно допилить до синтезируемости, правда работать будет тактов 10-15, но всё же будет легковеснее типичных IPкоров для этой же цели.

I-Love-Microsoft ★★★★★
() автор топика
Ответ на: комментарий от iVS

(1+t)/(1-t)=x => t=(x-1)/(x+1)

Объясни, Распиши это преобразование, вроде вижу, что все ясно как арбуз, а парсер ломается, аж стыдно :-D

Twissel ★★★★★
()
Последнее исправление: Twissel (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.