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 👍!