Форум программистов, компьютерный форум, киберфорум
Assembler: математика, вычисления
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.85/34: Рейтинг темы: голосов - 34, средняя оценка - 4.85
2 / 2 / 0
Регистрация: 05.09.2020
Сообщений: 18

Быстрая функция вычисления логарифма с одиночной точностью

12.09.2020, 12:48. Показов 7425. Ответов 26
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Отписываюсь о результатах. Идеально подошло решение со статическими константам - к ним применимо выражение offset.
Привожу также весь код функции - возможно, он кому-то окажется полезным, тем более, что в интернете я не встречал более эффективных решений.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// Функция быстрого вычисления натурального логарифма с одинарной точностью
// Для повышения производительности алгоритм не поддерживает денормализованные числа (они считаются нулём)
float ln(float x)
{
  static const float ct[5] =       // Константа ln 2 и коэффициенты степенного ряда b0...b3
  {
    0.439077714f,            // b3
    0.576395561f,            // b2
    0.961802196f,            // b1
    2.88539007f,             // b0
    0.693147181f             // ln(2)
  };
  _asm{
    mov eax, [x]             // Прочитать в eax двоичное представление числа x
    or edx, -1               // Инициализировать результат значением "NAN"
    shl eax, 1               // Сдвинуть число влево, чтобы весь порядок был в старшем байте
    jc ln_end                // Перейти в конец, если x<0 или x=-0, т.е. некорректный аргумент логарифма
    bswap eax                // Переставить байты числа для упрощённого доступа к старшим байтам
    cvtsi2ss xmm2, edx       // xmm2=-1
    movzx ecx, al            // Получить в ecx порядок числа со смещением +127
    shl edx, 23              // Поместить в результат -Inf на случай нуля или денормализованного числа
    jecxz ln_end             // Перейти в конец, если число денормализованное (т.е. близко к 0) или 0
    sub ecx, 127             // В ecx формируем правильный порядок числа (без смещения)
    sahf                     // Проверить старший бит явной части мантиссы: установить sf, если 1.5<=x<2
    mov al, 127              // Заменить порядок числа так, чтобы стало 1<=x<2
    jns ln_x_done            // Пропустить коррекцию порядка, если 1<=x<1.5
    dec eax                  // Для 1.5<=x<2 привести аргумент x к диапазону 0.75<=x<1
    inc ecx                  // Соответственно целая часть логарифма увеличивается на 1
      ln_x_done:             // Теперь 0.75<=x<1.5, как требуется для разложения функции в ряд Чебышёва
    bswap eax                // Восстановить правильный порядок байтов числа в eax
    cvtsi2ss xmm3, ecx       // В xmm3 - порядок числа, это будет добавка к логарифму (его целая часть)
    shr eax, 1               // Откатить сдвиг числа влево, теперь в eax двоичное представление числа x
    movd xmm0, eax           // xmm0=x (приведённое значение)
    movss xmm1, xmm0         // xmm1=xmm0=x
    addss xmm0, xmm2         // xmm0=x-1
    cdq                      // edx=0 - инициализировать счётчик цикла
    subss xmm1, xmm2         // xmm1=x+1
    divss xmm0, xmm1         // xmm0=(x-1)/(x+1)=u
    mov eax, offset ct       // В eax адрес таблицы констант
    movss xmm2, xmm0         // xmm2=u
    mulss xmm2, xmm2         // xmm2=u^2
    movss xmm1, [eax]        // Инициализировать сумму младшим коэффициентом: xmm1=b3
      ln_loop:               // Цикл вычисления полинома
    inc edx                  // Увеличить счётчик цикла на 1. Флаг pf установится только при edx = 3
    mulss xmm1, xmm2         // Умножаем предыдущий результат на u^2
    addss xmm1, [eax+4*edx]  // Прибавляем текущий коэффициент (схема Горнера)
    jnp ln_loop              // Учесть все 4 коэффициента
    mulss xmm0, xmm1         // Вычислить в xmm0 результат lb(x)
    addss xmm0, xmm3         // Прибавить к логарифму порядок числа
    mulss xmm0, [eax+16]     // Перевести двоичный логарифм в натуральный
    movd edx, xmm0           // Переместить результат в edx
      ln_end:                // Перенос результата в стек сопроцессора
    mov [x], edx             // Сохранить результат на месте переменной x
    fld [x]                  // Затем загрузить его в стек FPU в соответствии с соглашениями среды
    }
}
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
12.09.2020, 12:48
Ответы с готовыми решениями:

Нужно вывести на экран число e (основание натурального логарифма) с точностью до десятых
На языке Swift, я новичок в Swift

Вычислить значение натурального логарифма с точностью до ε=10-7 через суммы трех рядов:
Добрый день! Никак не могу сообразить, как написать такую программу. Вычислить значение натурального логарифма с точностью до ...

Описать функцию вычисления логарифма
Помогите описать функцию вычисления логарифма, обработать ошибку вычисления логарифма 0

26
E=m*c^2
 Аватар для K_ILYA_V
160 / 47 / 10
Регистрация: 04.02.2019
Сообщений: 263
Записей в блоге: 5
06.10.2020, 15:42
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от VTsaregorodtsev Посмотреть сообщение
Это если в функции локальных переменных нет.
У Вас же в коде есть float buf - поэтому, наверное, пролог-эпилог таки будут созданы несмотря на директиву __declspec(naked).
buf создается как локальная переменная в стеке еще до вызова функции.

Assembler
1
2
3
4
5
    out_f = ln(in_f);
00AA1070  push        ecx  
00AA1071  mov         dword ptr [esp],41200000h  
00AA1078  call        ln (0AA1000h)  
00AA107D  fstp        dword ptr [out_f (0AA3384h)]
сперва компилятор пушит регистр чтобы сдвинуть стек а потом укладывает в него локальную копию переменной, а пролог/эпилог он создает уже в самой функции? ее я и вынимаю в регистр через [esp + 4]

Добавлено через 5 минут
без __declspec(naked) для __cdecl компилятор создаст

Assembler
1
2
3
00321000  push        ebp  
00321001  mov         ebp,esp  
00321003  push        ecx
на входе и

Assembler
1
2
3
00321072  mov         esp,ebp  
00321074  pop         ebp  
00321075  ret         4
на выходе
0
2 / 2 / 0
Регистрация: 05.09.2020
Сообщений: 18
06.10.2020, 20:11  [ТС]
Выкладываю аналогичную функцию логарифма с двойной точностью. Оптимизации особо не делал. Главные оптимизации, которые можно сделать, - разворачивание цикла и передача аргумента/результата через xmm0.

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// Функция быстрого вычисления натурального логарифма с двойной точностью
// Для повышения производительности алгоритм не поддерживает денормализованные числа (они считаются нулём)
double ln(double x)
{
  static const double ct[10] =          // Таблица констант
  {
    1.414213562373095049,               // 2^0.5
    2.885390081777926811,               // b0
    0.9617966939259898017,              // b1
    0.5770780163454805578,              // b2
    0.4121985858489949842,              // b3
    0.3205985341276662424,              // b4
    0.2623343917068792287,              // b5
    0.2209121151421818382,              // b6
    0.2136683673423465570,              // b7
    0.6931471805599453094               // ln 2
  };
  _asm
  {
    movsd xmm0, [x]                     // xmm0 = x
    vmovsd xmm0, [x]                    // xmm0 = x
    or edx, -1                          // edx = -1
    vpextrw eax, xmm0, 3                // Прочитать в ax старшее слово числа, содержащее знаковый бит и порядок
    sahf                                // Проверить знаковый бит числа
    js ln_break                         // Перейти для обработки ошибки, если x <= -0
    mov ecx, 0x00003FF0                 // Выставить в ch страший байт порядка и подготовить маску в cl
    or cl, al                           // Выставить младший байт порядка x
    shr eax, 4                          // Получить в eax порядок числа плюс смещение 1023
    jnz ln_ok                           // Перейти, если x>0 и не является денормализованным
    mov dl, 240                         // Подготовить в dx старшее слово значения -Inf
      ln_break :                        // Здесь формируется результат в случае ошибки
    vmovd xmm0, edx                     // Поместить edx в младшую часть xmm0
    vpsllq xmm0, xmm0, 48               // Сдвинуть слово 0xFFFF или 0xFFF0 в старшую часть числа
    jmp ln_end                          // Перейти в конец функции
      ln_ok :                           // Продолжаем с допустимым значением x > 0
    vcvtsi2sd xmm2, xmm2, edx           // xmm2 = -1
    vpinsrw xmm0, xmm0, ecx, 3          // Заменить порядок числа x. Теперь 1 <= x < 2
    mov edx, offset ct                  // В edx адрес таблицы констант
    sub eax, 1023                       // Вычислить в eax порядок без смещения; это будет добавка к логарифму
    vcomisd xmm0, [edx]                 // Сравнить x и sqrt(2)
    jc ln_x_done                        // Перейти, если 1<=x<sqrt(2), т.е. не требуется коррекция порядка
    and ecx, -17                        // Вычесть из порядка 1, чтобы стало sqrt(2)/2 <= x < 1
    inc eax                             // Добавка к логарифму соотвтетственно увеличивается на 1
    vpinsrw xmm0, xmm0, ecx, 3          // Обновить значение xmm0
      ln_x_done :                       // В xmm0 приведённое значение x, такое что sqrt(2)/2 <= x < sqrt(2)
    vcvtsi2sd xmm3, xmm3, eax           // В xmm3 добавка к двоичному логарифму
    vsubsd xmm1, xmm0, xmm2             // xmm1 = x+1
    vaddsd xmm0, xmm0, xmm2             // xmm0 = x-1
    shr ecx, 11                         // ecx = 7 - инициализируем счётчик цикла
    vdivsd xmm0, xmm0, xmm1             // xmm0 = (x-1)/(x+1) = u
    vmovsd xmm1, [edx+64]               // Инициализировать сумму младшим коэффициентом: xmm1 = b7
    vmulsd xmm2, xmm0, xmm0             // xmm2 = u^2
      ln_loop :                         // Цикл вычисления полинома (ecx пробегает от 7 до 1)
    vfmadd213sd xmm1, xmm2, [edx+ecx*8] // Обновляем сумму ряда (схема Горнера)
    loop ln_loop                        // Учесть все 8 коэффициентов
    vfmadd213sd xmm0, xmm1, xmm3        // Получить в xmm0 двоичный логарифм и прибавить к нему порядок
    vmulsd xmm0, xmm0, [edx+72]         // Перевести двоичный логарифм в натуральный
      ln_end :                          // Перенос результата в стек сопроцессора в соответствии с соглашениями среды
    vmovsd [x], xmm0                    // Сохранить результат на месте переменной x
    fld [x]                             // Затем загрузить его в стек FPU
  }
}
1
3134 / 1731 / 273
Регистрация: 19.02.2010
Сообщений: 4,526
06.10.2020, 20:17
Цитата Сообщение от K-ILYA-V Посмотреть сообщение
buf создается как локальная переменная в стеке еще до вызова функции.
Занятно мелкософтовский транслятор работает.
Если бы вызываемая функция была в другом модуле (т.е. в месте вызова функции была бы известна только её сигнатура из инклюда - а о локальных переменных в ней ничего бы не было известно) - тогда бы компилятор сгенерил бы другой код?
(это я так - просто мысли вслух)

Цитата Сообщение от K-ILYA-V Посмотреть сообщение
ее я и вынимаю в регистр через [esp + 4]
Я бы тут адресовался к имени аргумента - чтобы компилятор сам высчитал нужное смещение независимо от того, был ли сгерерирован пролог у функции (в котором идёт пуш в стек, может быть, даже неединственный пуш), и сколько других пушей в стек возникло в коде до места обращения к аргументу функции.
0
E=m*c^2
 Аватар для K_ILYA_V
160 / 47 / 10
Регистрация: 04.02.2019
Сообщений: 263
Записей в блоге: 5
06.10.2020, 20:22
Цитата Сообщение от VTsaregorodtsev Посмотреть сообщение
Я бы тут адресовался к имени аргумента - чтобы компилятор сам высчитал нужное смещение...
не могу с вами не согласиться. даже стало интересно как именно он тогда напишет адресацию.
0
2 / 2 / 0
Регистрация: 05.09.2020
Сообщений: 18
06.10.2020, 20:25  [ТС]
Первая команда внутри ассемблерного кода написана по ошибке (она дублирует вторую команду), её нужно исключить.
0
E=m*c^2
 Аватар для K_ILYA_V
160 / 47 / 10
Регистрация: 04.02.2019
Сообщений: 263
Записей в блоге: 5
06.10.2020, 20:32
Цитата Сообщение от Aenigma Посмотреть сообщение
Выкладываю аналогичную функцию логарифма с двойной точностью. Оптимизации особо не делал. Главные оптимизации, которые можно сделать, - разворачивание цикла и передача аргумента/результата через xmm0.
похоже двойная точность требует двойного времени, в то время как наш враг fldln2 не дремлет и бахает что одинарную точность что двойную за один и тот же отрезок времени.

походу только возможность вычислить два числа одновременно может оправдать векторизацию процесса.
0
2 / 2 / 0
Регистрация: 05.09.2020
Сообщений: 18
06.10.2020, 20:42  [ТС]
K-ILYA-V, я сравнивал производительность с x87-вариантом без оптимизаций. Когда делал на SSE2, то на некоторых процессорах производительность была примерно одинаковая, на некоторых - до 2-х раз быстрее, чем на x87. На AVX+FMA и с оптимизациями преимущество будет более очевидным на всех процессорах. Главная оптимизация - убрать бессмысленную пересылку результата xmm0 -> память -> FPU -> память -> xmm0. Именно так и происходит при работе функции с ключом /o2.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
06.10.2020, 20:42

Модуль для вычисления логарифма
Создать свой модуль для вычисления логарифма числа и программу, проверяющую работоспособность модуля: вычисление значений функции...

Вычислить число e (основание натурального логарифма) с точностью n чначащих десятичных цифр после запятой
Добрейшего времени суток, необходима помощь в решении задачи на с++. Сама задача: &quot;Вычислить число e (основание натурального...

функция логарифма
какая функция в С++ функция логарифма? и как она используется

Используя функцию для вычисления логарифма, найти значения выражения
используя функцию для вычисления логарифма, найти значения выражения (loga(b))^x+(logb(a))^1/x.

Алгоритм вычисления логарифма без использования разложения в ряд Тейлора
Необходим алгоритм вычисления log a b, можем преобразовать как log e b / log e a Задача сводится к вычислению натурального логарифма, но...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
27
Ответ Создать тему
Новые блоги и статьи
Мастера простых решений
DevAlt 23.08.2026
В сишарп стэках winforms, да и wpf существует сложная система связывания источниках данных и элементов формы(текстовые поля и метки), опирается все это на технологию событий и мета. . .
Цена ошибки
DevAlt 23.08.2026
Человек я беспокойный и потому заинтересовался OCaml, в чате форсили функторы модулей как суперфичу. Пытаясь отдуплить концепт, наткнулся на тутор с простым примером. А главный принцип обучения от. . .
Сегодня суббота, 22.08.2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины.
zorxor 22.08.2026
Сегодня суббота, 22. 08. 2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины. Кто Я, откуда Я пришел и куда Я иду? Эти вопросы не оставляют меня ни на секунду. Жизнь на планете Земля. . .
Жизня: рисунок укладки багажа, сделанный клодом
anaschu 21.08.2026
Сделал 15 снимков, он по снимкам сделал схему.
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
Когда логика программы не спасает от человеческих ошибок
Maks 18.08.2026
В последнее время всё чаще и чаще сталкиваюсь с таким явлением, как абсолютная невнимательность (или глупость) пользователей. Проявляется это чаще всего на работе в коллективе. Допустим, человек с. . .
Лето уходит
kumehtar 17.08.2026
Мысли в слух
kumehtar 17.08.2026
Забавно, насколько сейчас стала доступна информация. Например о магии, духовном развитии, медитациях, и других подобных направлениях, ранее зачастую тайных, передаваемых от учителя к ученику. Хотя. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru