I had a requirement in one tool that I was working to find whether a given table column was power of 2.
The easiest way to check would be using the LOG(2,y). If the result was an integer, the number would be power of 2.
2^X = Y -> LOG(2,Y)=X
And to check if X was an integer, I would add a predicate of TRUNC(X) = X.
SQL> create table mytab (a number); Table MYTAB created. SQL> insert into mytab select rownum from dual connect by level <= 100; 100 rows inserted. SQL> commit; Commit complete.
However, when I tried to check the results, I got a weird result:
SQL> select * from mytab where log(2,a)=0;
A
----------
1
SQL> select * from mytab where log(2,a)=1;
A
----------
2
SQL> select * from mytab where log(2,a)=2;
no rows selected
SQL> select * from mytab where log(2,a)=3;
no rows selected
After some research, I found this article (https://community.oracle.com/tech/developers/discussion/4198779/floor-log-x-y-providing-wrong-values-in-some-cases) which explains that due to computing limitations, LOG(x,y) is always computed as LN(y)/LN(x), no matter what x and y. Thus, when I run log(2,4), instead of getting 2, I get a different result:
SQL> select log(2,4) from dual; 1.99999999999999999999999999999999999998 SQL> select log(2,8) from dual; 2.99999999999999999999999999999999999999 SQL> select log(2,16) from dual; 3.99999999999999999999999999999999999998
So, to solve the problem, I need to do a round on the result number, on the 16+ decimal case just to avoid a false positive:
SQL> select * from mytab where round(log(2,a),16)=2;
A
----------
4
SQL> select * from mytab where round(log(2,a),16)=3;
A
----------
8
So, in the end, to get only the rows whether a given column was power of 2, I would run:
SQL> select * from mytab where round(log(2,a),16)=trunc(round(log(2,a),16));
A
----------
1
2
4
8
16
32
64
7 rows selected.
Have you enjoyed? Please leave a comment or give a ?!




