LOG(x,y) returning wrong results in some cases

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

Leave a Reply

Your email address will not be published.