powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / Oracle [игнор отключен] [закрыт для гостей] / msb (most significant bit) на pl/sql есть готовый?
12 сообщений из 87, страница 4 из 4
msb (most significant bit) на pl/sql есть готовый?
    #40000328
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
booby
прямая манипуляция.

Прямая манипуляция битами над числом с плавающей точкой - нонсенс.
...
Рейтинг: 0 / 0
msb (most significant bit) на pl/sql есть готовый?
    #40000340
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
точность формулировки не имеет значения.
Значение имеет то, что bitand над number-ом "безумно дорог".
...
Рейтинг: 0 / 0
msb (most significant bit) на pl/sql есть готовый?
    #40000352
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
andrey_anonymous
xtender
пропущено...
если уж совсем заморочиться, я бы сделал сишную функцию доставания экспоненты для любых типов

Ага, и прилинковал бы ее к оракелю как когда-то Бегун тут выделывался :)


И так можно? Интересно, как будет выглядеть передача параметра NUMBER переменной длины.
...
Рейтинг: 0 / 0
msb (most significant bit) на pl/sql есть готовый?
    #40000438
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Если важна точность (т.е. чтоб 2^N-1 и 2^N различало), то вот самое короткое, что работает по 2^128 включительно.

Код: plsql
1.
2.
3.
trunc(case when val < 4398046511104 then log(2,val+0.5)
           when val < 19342813113834066795298816 then 42+log(2,trunc(val/4398046511104)+0.5)
           else 42+42+log(2,trunc(val/19342813113834066795298816)+0.5) end)



Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
with pow2 (val) as
(
  select 2 as val from dual union all
  select 2*val    from pow2 where val <= 170141183460469231731687303715884105728
),
 args as (select val-1 as val from pow2 union select val from pow2)
-- константы посвящаются Дугласу Адамсу
select val, trunc(case when val < 4398046511104 then log(2,val+0.5)
                   when val < 19342813113834066795298816 then 42+log(2,trunc(val/4398046511104)+0.5)
                   else 42+42+log(2,trunc(val/19342813113834066795298816)+0.5) end)
from args;
...
Рейтинг: 0 / 0
msb (most significant bit) на pl/sql есть готовый?
    #40000531
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
в выложенном мной коде была ошибка в шаге вычисления.
выкладываю исправление, для порядка

Код: plsql
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.
-- получает двоичную степень числа, соответствующую 2**msb (most significant bit) входного параметра
-- (под диапазон значений 0 - 170141183460469231731687303715884105727 - power(2,127)-1 )
  function msb(px in Number) return number deterministic parallel_enable
  is
     offs Number;
     n_mask Number;
     x Number;
     x2 Number; 

     -- шаг вычисления, удваивающего число единиц от msb вправо
     Procedure step_mask 
     Is
     Begin      
      x := x2; n_mask := floor(x/offs);
      x2 := x - bitand(x, n_mask) + n_mask;   -- вычисляет битовый OR           
      offs := offs*offs;
     End;    
  begin

    if px = 0 Or px Is Null Then Return px; End if;
   
    offs := 2;
    x2 := floor(px); 
   
    Loop 
      -- 1 (2 соседних к msb bit-ов установлены в 1)
      step_mask;
      Exit When x=x2;
                
      -- 2 (4 соседних к msb bit-ов установлены в 1)
      step_mask; 
      Exit When x=x2;
          
      -- 3 (8 соседних к msb bit-ов установлены в 1)
      step_mask; 
      Exit When x=x2;
      
      -- 4 (16 соседних к msb bit-ов установлены в 1)
      step_mask; 
      Exit When x=x2;

      -- 5 (32 соседних к msb bit-ов установлены в 1)
      step_mask; 
      Exit When x=x2;      

      -- 6 (64 соседних к msb bit-ов установлены в 1)
      step_mask; 
      Exit When x=x2;

      -- 7 (128 соседних к msb bit-ов установлены в 1)
      step_mask; 
      Exit;
    End Loop;    
    -- гарантированы все единицы, начиная с msb
    -- переходим в следующий разряд и возвращаем половину
    Return floor((x2+1)/2);
  end;  

...
Рейтинг: 0 / 0
msb (most significant bit) на pl/sql есть готовый?
    #40000580
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Мне очень нравится решение Booby с развернутым циклом.

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

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
create or replace function aa_topbit_noloop(v in number) return integer deterministic is
  m number;
  b06 constant integer := 64;
  b12 constant integer := 4096;
  b18 constant integer := 262144;
  b24 constant integer := 16777216;
  b28 constant integer := 268435456;
  b56 constant number  := 72057594037927936;
  b94 constant number  := 19342813113834066795298816;
  bb2 constant number  := 5192296858534827628530496329220096;
  x binary_integer;
  n binary_integer;
  l binary_integer;
begin
  m := case when v >= bb2 then bb2 when v >= b94 then b94 when v >= b56 then b56 when v >= b28 then b28 else 1 end;
  x := v/m;
  n := case when x >= b24 then b24 when x >= b18 then b18 when x >= b12 then b12 when x >= b06 then b06 else 1 end;
  x := x/n;
  l := case when x >= 32 then 32 when x >= 16 then 16 when x >= 8 then 8 when x >= 4 then 4 when x >= 2 then 2 else 1 end;
  return l*n*m;
end aa_topbit_noloop;



Вопрос к знатокам - инициализация констант в декларациях функции занимает время, или это оптимизируется, т.к. константы?

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
SQL> declare
  2    val number;
  3  begin
  4    for i in 1..1000000
  5    loop
  6      val := aa_topbit_noloop( i+70000000000000000000000000000000000000 );
  7    end loop;
  8  end;
  9  /
PL/SQL procedure successfully completed
Executed in 4,813 seconds



Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
SQL> declare
  2    val number;
  3  begin
  4    for i in 1..1000000
  5    loop
  6      val := msb( i+70000000000000000000000000000000000000 );
  7    end loop;
  8  end;
  9  /
PL/SQL procedure successfully completed
Executed in 34,641 seconds
...
Рейтинг: 0 / 0
msb (most significant bit) на pl/sql есть готовый?
    #40001095
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Итог пока такой:
остановился промежуточно на комбинации захардкоженного
двоичного поиска в комбинации с поиском по двум массивам на 256 значений в "последнем байте".
(процедура возвращает и показатель степени и её значение).

Не готов подтвердить "медленность массивов" - ровно то же время поиска по двум массивам,
с оговоркой на погрешности измерений, что и прямой двоичный поиск на байте.
Время против "классического" варианта с bitand-ом сократилось процентов на 40.

В общем, теперь осталось только понять, пойдет ли оно в дело.

2НеофитSQL
насколько быстро сможете найти ошибку в своем коде?
(внимательно не смотрел, но подозреваю, что она на втором шаге).

PS
Вообще, "изящный код" и pl/sql - антонимы.
Правильный для pl/sql девиз - "пишите проще, а лучше всего - как все ."
...
Рейтинг: 0 / 0
msb (most significant bit) на pl/sql есть готовый?
    #40001147
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
booby,

Я пока нашел у себя ошибку на 0 шаге (нет проверки на ноль).

Пока не подсказывайте, я подумаю.
...
Рейтинг: 0 / 0
msb (most significant bit) на pl/sql есть готовый?
    #40001148
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
НеофитSQL,

Вы не подозревайте, что я знаю, где она точно.
я просто вижу, что она есть и носит систематический и серийный характер.


Так-то я сам не пишу код влёт без ошибок, и не могу показать пальцем, по крайней мере на лиц мужского пола,
обладающих таким достоинством.
Среди женщин-программистов такие вершины внимания и скурпулёзности допустимы,
но и у них - такой недостаток большая редкость.
(Последний раз что-то похожее на Аду Лавлейс я встречал уже очень много лет назад).


Ошибка - обычное житейское дело.
Как вы планируете жить с нею, с учётом того, что вы всех своих ошибок точно не выловите,
и какие-то из них достанутся следующему товарищу, вот в чём вопрос.

Хорошо, когда прямо глазом глядя в код, можно доказать его правильность.
Если код настолько изящен, что методом чтения кода выявить ошибку не удается,
то в дело вступают прогоны под отладчиком.
Но по настоящему изящному коду и отладчик нипочем.

Тогда остаётся действовать дедовскими способами.
(В старину, кстати, программист опознавался не по наличию или отсутствию пиджака, свитера,
или смешной шапочки на голове, а по наличию сразу нескольких точёных простых карандашей в нагрудном кармане.)
Так вот дедовский способ заключается в использовании карандаша и листка бумаги, на котором рисуются загогулины,
и после чего изящный код выбрасывается в помойку и заменяется на пригодный к сопровождению.
Как-то так.

По делу - проверьте, что возвращается для значений 101, 1001, 10001, 10...01 и соседних.
В первой сотне лаг начинается с 96.
...
Рейтинг: 0 / 0
msb (most significant bit) на pl/sql есть готовый?
    #40001149
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Нашел! Без использования trunc(), присваивание дробного числа целой переменной не обрезает дробную часть, а округляет, по всей видимости.

Другими словами, int n := 127/64 даст двойку, а не единицу.
Это важное отличие Pl/SQL от других мне известных языков.

Добавил trunc() - функция заработала еще быстрее, и уже без ошибок :)

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
SQL> set timing on
SQL> 
SQL> declare
  2    val number;
  3  begin
  4    for i in 1..1000000
  5    loop
  6      val := aa_topbit_noloop( i+70000000000000000000000000000000000000 );
  7    end loop;
  8  end;
  9  /
PL/SQL procedure successfully completed
Executed in 3,375 seconds



Меньше 4 микросекунд на вызов.
Для сравнения, вызов пустой функции занимает около 0.8 микросекунд, т.е. инлайн ускорит еще процентов на 20, до < 3 мкс.
...
Рейтинг: 0 / 0
msb (most significant bit) на pl/sql есть готовый?
    #40001364
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
в общем, поковыряв немного, решил в дело не пускать.
Последнее, с чем игрался выглядит так:
спецификация пакета
Код: plsql
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.
create or replace package msb_pkg is

  -- Author  : booby
  -- Created : 2020-09-22 12:23:08
  -- Purpose : поддержка операций получения most significant bit 
  ----------------------------------------------------------------------


 -- инициализация внутреннего состояния пакета
 procedure init_msb; 
 ----------------------------------------------------------------
 -- вычисление msb (most significant bit), процедура
 -- проектировалось под диапазон значений 0 - 170141183460469231731687303715884105727 - power(2,127)-1 
 -- выбрасывает ошибку -20203 при потере состояния пакетом,
 -- или подаче на вход "слишком большого" числа ( > 128 разрядов) 
 -- msb_pkg.msb_2p(px in Number,msb Out nocopy Number,i_exponent Out Nocopy Pls_Integer);
 Procedure msb_2p(px in Number, -- входное значение
                  msb Out nocopy Number, -- значение степени для msb
                  i_exponent Out Nocopy Pls_Integer -- показатель степени
                  );  
 -----------------------------------------------------------------
-- вычисление msb (most significant bit)
-- проектировалось под диапазон значений 0 - 170141183460469231731687303715884105727 - power(2,127)-1 
-- получает двоичную степень числа, соответствующую 2**(старший_разряд)
-- msb_pkg.msb_t(px in Number)
  function msb_t(px in Number) return number deterministic parallel_enable;
 ------------------------------------------------------------------ 

end msb_pkg;
/



тело

Код: plsql
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.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
create or replace package body msb_pkg is
  
  TYPE T_MSB8_MAP Is Table Of pls_integer index by pls_integer;
  a_msb_val T_MSB8_MAP; -- для значений msb в интервале 1 - 255
  a_msb_exp T_MSB8_MAP; -- для значений показателя степени в интервале 1 - 255
  -----------------
 -- инициализация внутреннего состояния пакета
 procedure init_msb
 Is
 -- только для инициализации a_msb_val
 -- (возвращает 1 для x2=0)
   Function msb8(x2 in pls_integer) Return pls_integer
   Is     
   Begin   
     
     Return Case When x2 >= 16
                Then
                Case When x2 >= 128 Then 128
                     When x2 >= 64 Then 64
                     When x2 >= 32 Then 32
                Else 16
                End
              When x2 > 7 Then 8
              When x2 > 3 Then 4
              When x2 > 1 Then 2    
              Else 1
            End;            
    End;
 Begin
   
   For i in 0..255    
   Loop
     -- a_msb_val(0) = 1 -- в нулевом индексе единица,
     -- (не использовать массив вне контекста msb_inner)
     a_msb_val(i) := msb8(i);
     a_msb_exp(i) := Case 
                       When i Between 0 And 1 Then 0
                       When i Between 2 And 3 Then 1
                       When i Between 4 And 7 Then 2
                       When i Between 8 And 15 Then 3
                       When i Between 16 And 31 Then 4
                       When i Between 32 And 63 Then 5
                       When i Between 64 And 127 Then 6
                       When i Between 128 And 255 Then 7      
                     End;
   End Loop;    
 End;    
 -----------------------------------------------------------------
 -- msb_pkg.msb_2p(px in Number,msb Out nocopy Number,i_exponent Out Nocopy Pls_Integer); 
 Procedure msb_2p(px in Number, -- входное значение
                  msb Out nocopy Number, -- значение степени для msb
                  i_exponent Out Nocopy Pls_Integer -- показатель степени
                  )
 Is 
    i pls_integer := 0;
    k pls_integer := 0;
    
    x Number;
    x2 pls_integer;
    --
    m Number := 1.0;
    t Number := 1.0;
    
  begin  
      -- из агрегатной функции Null сюда не приедет.    
      -- оставлено на случай прямого использования. 
    If Coalesce(px, 0) = 0 Then      
      msb := 0; -- спорное место для (px Is Null), экономим if-ы в местах использования
      i_exponent := 0;
      Return;
    End If;  
    
    x := Floor(px);
    -- 
    Case 
      When x < 18446744073709551616 -- 2**64
          Then -- [0 -- 2**64) -- младшие разряды
         Case When x < 4294967296 -- 2**32, i := 32
             Then -- [0 - 2**32)
             Case When x < 65536 -- 2**16
                 Then -- [0 - 2**16)
               Case When x < 256 -- 2**8
                   Then
                 i:=0;
               Else  
                 i:= 8;
                 m:= 256;
               End Case;  
             When x < 16777216 -- 2**24 
                   Then -- [2**16 - 2**24) 
                   i := 16;
                   m := 65536;                 
             Else  -- [2**24 - 2**32)
                   i := 24;
                   m := 16777216;            
             End Case;
         Else 
           -- [2**32 - 2*64)
           Case 
         When x < 281474976710656 -- 2**48 
             Then -- [2**32 - 2**48)
             Case When x < 1099511627776 -- 2**40
               Then
                 i := 32;
                 m := 4294967296;
             Else
                 i := 40;
                 m := 1099511627776;
             End Case;      
             
         -- [2*48 - 2**64)
         When x < 72057594037927936 -- 2*56 
               Then -- [2**48 - 2**56)
                i := 48;
                m := 281474976710656; -- 2**48;  
         Else -- [2**56 - 2**64)
                i := 56;
                m := 72057594037927936; -- 2**56      
          End Case;  
         End Case;        

     -- старшие разряды - между [2**64 - 2**128)
     When x < 79228162514264337593543950336    -- 2**96
         Then -- [2**64 - 2**96)
           Case When x < 1208925819614629174706176  -- 2**80 
             Then -- [2**64 - 2**80)               
             Case When x <  4722366482869645213696  -- 2**72  
               Then -- [2**64 - 2**72)
                i := 64;
                m := 18446744073709551616;
             Else -- [2**72 - 2**80)
                i := 72;
                m := 4722366482869645213696;  
             End Case;    
           Else 
             -- [2**80 - 2**96)
             Case When x < 309485009821345068724781056   -- 2**88
               Then -- [2*80 - 2*88)
               i := 80;
               m := 1208925819614629174706176;  -- 2**80
             Else
               -- [2**88 - 2**96)
               i := 88;
               m := 309485009821345068724781056; -- 2**88
             End Case;       
           End Case;  -- 2**80 - 2*96    

     -- [2**96 - 2**128) 
     When x <  5192296858534827628530496329220096 -- 2**112
           Then -- [2**96 - 2**112)
           Case When x < 20282409603651670423947251286016  -- 2**104    
             Then -- [2**96 - 2**104)
               i := 96;
               m := 79228162514264337593543950336; -- 2**96 
           Else
             -- [2**104 - 2**112)
               i := 104;
               m := 20282409603651670423947251286016;
           End Case;      

     -- [2**112 - 2*128)         
     When x < 1329227995784915872903807060280344576   -- 2**120 
             Then -- [ 2**112 - 2**120)
               i := 112;
               m := 5192296858534827628530496329220096; 
           Else
                 -- [2**120 - 2**128)
               i := 120;
               m := 1329227995784915872903807060280344576;      
    End Case;  
    x2 := case when i > 0 Then floor(x/m ) else x End;
    --
    If x2 > 1 Then   
      Begin 
        t := a_msb_val(x2);
        k := a_msb_exp(x2);
      Exception
        When no_data_found Then
          -- ошибка программиста: потеряно состояние пакета или на входе число вне ожидаемого диапазона.
          Raise_Application_Error(-20203
                                 , 'msb_pkg.msb_inner.PE.1: index <'||x2        
                                 ||'> out of bounds (0-256) or array state lost, px='||px
                                 ||' .First='||a_msb_val.First
                                 ||' .Last='||a_msb_val.Last
                                 , True
                                 );
      End;
    End If;     
    
    msb := m*t; 
    i_exponent := (i+k);
    
 End;                         
                     
 ---------------------------------------------------------------
  function msb_t(px in Number) return number deterministic parallel_enable  
  is
  
    i pls_integer;
    m Number;
        
  begin 
    PRAGMA INLINE(msb_2p,'YES'); 
    msb_2p(px => px
          , msb => m
          , i_exponent => i
          );
    Return m;  

  End;    
 ---------------------------------------------------------------
begin
  -- Initialization
  init_msb;
end msb_pkg;
/



тестовый запрос из топика

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
with 
  t(n) as (select level from dual connect by level<=5e3) 
 ,s0(x) as (select t1.n*1e6 + t2.n as x from t t1,t t2) -- 5e3^2 
select 
   -- count(*) as rn, to_char( max(x), 'TM9') ,
   to_char(sum(msb_pkg.msb_t(x)),'TM9') sum_msb
from s0 
;



еще раз всем спасибо.
...
Рейтинг: 0 / 0
msb (most significant bit) на pl/sql есть готовый?
    #40005028
НеофитSQL
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Читал книжку по SQL, понял что моя 4-микросекундная функция ездила с тормозами.
Убрал тормоза - ускорилась до одной микросекунды.
...
Рейтинг: 0 / 0
12 сообщений из 87, страница 4 из 4
Форумы / Oracle [игнор отключен] [закрыт для гостей] / msb (most significant bit) на pl/sql есть готовый?
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]