Convert Binary's 2's compliment to decimal in oracle SQL - oracle

I have a sample number in a column of oracle table which is binary's 2's complimanet -
e.g 0110001000110111
I want to convert this to normal decimal number in 2's compliment.
Expected output-
Reference link - https://www.rapidtables.com/convert/number/decimal-to-binary.html

You can loop as applying powers of 2 while multiplying by each bit(0 or 1) starting from the right end of your presented value such as
SET SERVEROUTPUT ON
DECLARE
bin_nr VARCHAR2(100) := '0110001000110';
dec_nr NUMBER;
BEGIN
FOR i IN 1..LENGTH(bin_nr)
LOOP
dec_nr := NVL(dec_nr,0) + SUBSTR(bin_nr,-i,1)*(2**(i-1));
END LOOP;
DBMS_OUTPUT.PUT_LINE(dec_nr);
END;
/
which results 3142 as the decimal value.
Demo

For 2s compliment, the most-significant bit represents the sign bit and if that is 1 then you have a negative number:
DECLARE
bin_nr VARCHAR2(100) := '1111001110111010';
sign PLS_INTEGER;
dec_nr PLS_INTEGER;
BEGIN
IF SUBSTR(bin_nr, 1, 1) = '1' THEN
bin_nr := TRANSLATE(bin_nr, '01', '10');
sign := -1;
dec_nr := 1;
ELSE
sign := 1;
dec_nr := 0;
END IF;
FOR i IN 1 .. LENGTH(bin_nr) LOOP
IF SUBSTR(bin_nr, -i, 1) = '1' THEN
dec_nr := dec_nr + POWER(2, i-1);
END IF;
END LOOP;
dec_nr := dec_nr * sign;
DBMS_OUTPUT.PUT_LINE(dec_nr);
END;
/
Outputs -3142
If you are expecting an N-bit binary number as the input (for example, the link in the question expects a 16-bit binary number as an input for 2s compliment) then you should LPAD with zeroes if you have fewer than that many bits.
db<>fiddle here

Related

Can't display the final output once the user enters a negative number in PL/SQL

I can't display the final output once the user has entered a negative number.
I tried creating a program that will ask the user for numbers continuously. If the user enters a negative number, the program will stop running and will display the total, average, number of zeroes entered, highest and lowest number. Below is my written code:
SET SERVEROUTPUT ON;
DECLARE
n NUMBER(3);
total NUMBER(3) := 0;
numZeros NUMBER(3) := 0;
average NUMBER(3);
highest NUMBER(3) := 0;
lowest NUMBER(3) := 0;
BEGIN
LOOP
n := &n;
total := total + n;
IF n = 0 THEN
numZeros := numZeros + 1;
ELSIF n > highest THEN
high := n;
ELSIF n < lowest THEN
low := n;
END IF;
EXIT WHEN n < 0;
END LOOP;
average := total / n;
DBMS_OUTPUT.PUT_LINE('Total: ' || total);
DBMS_OUTPUT.PUT_LINE('Average: ' || average);
DBMS_OUTPUT.PUT_LINE('Number of zeros: ' || numZeros);
DBMS_OUTPUT.PUT_LINE('Highest number: ' || highest);
DBMS_OUTPUT.PUT_LINE('Lowest number: ' || lowest);
END;
The output throws an error saying how 'N' is not a procedure. I would appreciate some help in solving this issue.
There is a fundamental issue with your code, & is a substitution variable and is evaluated by the client application (i.e. SQL*Plus, SQL Developer or another client that supports substitution variables, which not all Oracle clients do) and is effectively implemented as if a find-replace was done on the source code.
Once the client has substituted the substitution variable then the statement is sent to the database where the PL/SQL engine will parse it. The PL/SQL engine never sees the substitution variable (because it is processed by the client application) and even if it did it would not understand how to handle it.
This means that you will be prompted for the replacement ONCE (and only once) and then that value will be used in every loop iteration so the loop will either never exit, if you enter a positive number first, or exit immediately, if you enter a negative number first.
If you want to enter a list of values then you need to enter them all at once:
SET SERVEROUTPUT ON;
DECLARE
items SYS.ODCINUMBERLIST := SYS.ODCINUMBERLIST(0,1,2,3,4,5);
total NUMBER(3,0) := 0;
numZeros NUMBER(3,0) := 0;
average NUMBER(5,2) := NULL;
highest NUMBER(3,0) := NULL;
lowest NUMBER(3,0) := NULL;
BEGIN
FOR i IN 1 .. items.COUNT LOOP
total := total + items(i);
IF items(i) = 0 THEN
numZeros := numZeros + 1;
END IF;
IF highest IS NULL OR items(i) > highest THEN
highest := items(i);
END IF;
IF lowest IS NULL OR items(i) < lowest THEN
lowest := items(i);
END IF;
END LOOP;
IF items.COUNT > 0 THEN
average := total / items.COUNT;
END IF;
DBMS_OUTPUT.PUT_LINE('Total: ' || total);
DBMS_OUTPUT.PUT_LINE('Average: ' || average);
DBMS_OUTPUT.PUT_LINE('Number of zeros: ' || numZeros);
DBMS_OUTPUT.PUT_LINE('Highest number: ' || highest);
DBMS_OUTPUT.PUT_LINE('Lowest number: ' || lowest);
END;
/
or, using SQL:
DECLARE
items SYS.ODCINUMBERLIST := SYS.ODCINUMBERLIST(0,1,2,3,4,5);
total NUMBER(3,0) := 0;
numZeros NUMBER(3,0) := 0;
average NUMBER(5,2) := NULL;
highest NUMBER(3,0) := NULL;
lowest NUMBER(3,0) := NULL;
BEGIN
SELECT COALESCE(SUM(column_value),0),
COUNT(CASE column_value WHEN 0 THEN 1 END),
AVG(column_value),
MAX(column_value),
MIN(column_value)
INTO total,
numZeros,
average,
highest,
lowest
FROM TABLE(items);
DBMS_OUTPUT.PUT_LINE('Total: ' || total);
DBMS_OUTPUT.PUT_LINE('Average: ' || average);
DBMS_OUTPUT.PUT_LINE('Number of zeros: ' || numZeros);
DBMS_OUTPUT.PUT_LINE('Highest number: ' || highest);
DBMS_OUTPUT.PUT_LINE('Lowest number: ' || lowest);
END;
/
fiddle
If you want to take user input then you can replace the initial lines with:
SET SERVEROUTPUT ON;
ACCEPT number_list CHAR PROMPT 'Enter a comma-delimited list of numbers:'
DECLARE
items SYS.ODCINUMBERLIST := SYS.ODCINUMBERLIST(&&number_list);
Change this line:
n := &n;
Remove n;. It should be:
n := &
I don't know if you made a mistake with this line:
high := n;
It should be:
highest := n;
Same with this line:
low := n;
which should be:
lowest := n;
After that, when I run the code, I get this compilation error:
ORA-06502: PL/SQL: numeric or value error: number precision too large
This is because PL/SQL has no capabilities for getting input from the user during execution of PL/SQL code.
Indeed the &amp is a feature of SQL*Plus and not of PL/SQL.
The loop simply keeps repeating with the first entered value (for &amp) until total becomes larger than 999.
Refer to Taking user input 'n' times PL/SQL
and PL/SQL: how do I prompt user input in a procedure?

PLSQL (finding range of prime number to 1000 )

DECLARE
n NUMBER;<br>i NUMBER;
pr NUMBER;
BEGIN
FOR n IN 2 .. 1000 LOOP
pr := 1;
FOR i IN 2 .. n / 2 LOOP
IF MOD(n, i) = 0 THEN
pr := 0;
END IF;
END LOOP;
IF (N = 997) THEN
DBMS_OUTPUT.PUT(n);
pr:=2;
ELSE
IF pr = 1 THEN
DBMS_OUTPUT.PUT(n||'&');
END IF;
END if;
END LOOP;
dbms_output.new_line;
END;
output should be as in one line----> 2&3&5&7&11&13&17&19&23&29&31&37&41&43&47&53&59&61&67&71&73&79&83&89&97&101&103&107&109&113&127&131&137&139&149&151&157&163&167&173&179&181&191&193&197&199&211&223&227&229&233&239&241&251&257&263&269&271&277&281&283&293&307&311&313&317&331&337&347&349&353&359&367&373&379&383&389&397&401&409&419&421&431&433&439&443&449&457&461&463&467&479&487&491&499&503&509&521&523&541&547&557&563&569&571&577&587&593&599&601&607&613&617&619&631&641&643&647&653&659&661&673&677&683&691&701&709&719&727&733&739&743&751&757&761&769&773&787&797&809&811&821&823&827&829&839&853&857&859&863&877&881&883&887&907&911&919&929&937&941&947&953&967&971&977&983&991&997
but not working in hackerrank compiler question is "Print Prime Numbers"
There's no problem in your code to produce the desired string except <br>i NUMBER;
Just comment out that piece in the declaration section as below :
DECLARE
n NUMBER; -- <br>i NUMBER;
pr NUMBER;
Here's a Demo for it.

PL/SQL Check Digit, luhn using MOD 11

So here is the question:
Write code to take in an id and determine if the check digit is correct
UPDATED CODE:
Set SERVEROUTPUT ON
DECLARE
val_num NUMBER := '&user_input';
holder NUMBER := 0;
y NUMBER := 0;
conv_string VARCHAR2(20);
BEGIN
conv_string := to_char(val_num*10);
for x in 1..length(conv_string) loop
y := to_number(substr(conv_string, -x, 1));
if mod(x,2) = 0 then
y := y * 2;
if y > 9 then
y := y - 9;
end if;
end if;
holder := holder + y;
end loop;
dbms_output.put_line ('Check is '||(11-Mod(holder, 11)));
END luhn;
/
SET SERVEROUTPUT ON
The return is:
SQL> # loop
Enter value for user_input: 036532
old 2: val_num NUMBER := '&user_input';
new 2: val_num NUMBER := '036532';
Check is 2
It should be 6
Before actual execution
SET SERVEROUTPUT ON
to enable SQL*Plus to fetch database output buffer.
Here is solution: https://community.oracle.com/thread/837639?start=0&tstart=0
There are lots of different variations of the luhn algorithm, so looking at these implementations and your (I think incomplete) description in the comments I think this may be fairly close to what you are looking for, and gives the correct checksum for 036532 as per your initial question.
Hope it is helpfull
Set SERVEROUTPUT ON
DECLARE
val_num number := '036532';
holder NUMBER := 0;
y NUMBER := 0;
conv_string VARCHAR2(20);
BEGIN
conv_string := to_char(val_num);
FOR X IN 1..LENGTH(CONV_STRING) LOOP
Y := TO_NUMBER(SUBSTR(CONV_STRING, -X, 1));
IF ((X+1) > 10) THEN
Y := Y * 10;
ELSE
Y := Y * (X + 1);
END IF;
IF (Y >= 10) THEN
HOLDER := HOLDER + TO_NUMBER(substr(TO_CHAR(Y), 1, 1)) + TO_NUMBER(substr(TO_CHAR(Y), 2, 1));
ELSE
HOLDER := HOLDER + Y;
END IF;
END LOOP;
HOLDER := MOD(HOLDER, 11);
Holder := 11 - mod(holder, 11);
dbms_output.put_line ('Check is '|| holder);
END luhn;
/
SET SERVEROUTPUT ON

How to print 74 Characters perline using base 64 algorithem in oracle pl/sql when working with clob datatype

Below is my code which will do the Image Coversion into base 64 algorithem.But I need 74 Characters per line. Existing base64 algoritehm is giving 64 characters per line.(UTL_ENCODE.base64_encode )
Image BLOB;
v_out_cl CLOB;
file_len PLS_INTEGER;
modulo PLS_INTEGER;
pieces PLS_INTEGER;
amt BINARY_INTEGER := 23808;
buf RAW (32767);
buf_tx varchar2(32767);
pos PLS_INTEGER := 1;
filepos PLS_INTEGER := 1;
counter PLS_INTEGER := 1;
DBMS_LOB.createtemporary (v_out_cl, TRUE, dbms_lob.call);
file_len := DBMS_LOB.getlength (Image );
modulo := MOD (file_len, amt);
pieces := TRUNC (file_len / amt);
WHILE (counter <= pieces) LOOP
DBMS_LOB.READ (Image, amt, filepos, buf);
buf_tx:=utl_raw.cast_to_varchar2
(UTL_ENCODE.base64_encode (buf));
dbms_lob.writeappend
(v_out_cl,length(buf_tx),buf_tx);
filepos := counter * amt + 1;
counter := counter + 1;
END LOOP;
IF (modulo <> 0) THEN
DBMS_LOB.READ
(image , modulo, filepos, buf);
buf_tx:=utl_raw.cast_to_varchar2
(UTL_ENCODE.base64_encode (buf));
dbms_lob.writeappend
(v_out_cl,length(buf_tx),buf_tx);
END IF;
I am unaware of any flags to change the line size of utl_encode.base64_encode. You'll probably have to resort to an additional copy pass. I haven't tested the impact on overall performance.
Under linux there is the base64 command that provides a flag to set the column width. Assuming you have the file 'col-64.b64' with 64 columns per line, the following command will transcode it to 74 cols per line:
base64 -d col-64.b64 | base64 -w 74 >col-74.b64
Note
In principle, there should be no need for a specific line length - The standard just says that characters outside the range of the encoding bytes (notably line separating CRLF characters) should be ignored.

factorial of a number in pl/sql

The following pl/sql program generates an error on execution on line the sum :=temp*sum; encountered symbol ; when expecting ( . Please explain my mistake.
declare
n number;
temp number;
sum number := 1;
begin
n := &n;
temp := n;
while temp>0 loop
sum := temp*sum;
temp := temp-1;
end loop;
dbms_output.put_line('Factorial of '||n||' is '||sum);
end;
/
Maybe not the answer to your question, but there is no need for PL/SQL here:
select round(exp(sum(ln(level))))
from dual
connect by level <= 5;
where 5 is your number (5!).
Additionally, if you like to operate faster in PL/SQL use pls_integer instead of number.
UPDATE
So according to comments I felt free to test:
create or replace package test_ is
function by_query(num number) return number deterministic;
function by_plsql(num number) return number deterministic;
end test_;
/
create or replace package body test_ is
function by_query(num number) return number deterministic
is
res number;
begin
select round(exp(sum(ln(level))))
into res
from dual
connect by level <= num;
return res;
end;
function by_plsql(num number) return number deterministic
is
n number := 0;
begin
for i in 1..num loop
n := n + ln(i);
end loop;
return round(exp(n));
end;
end test_;
So there are two functions with different content. Test query:
declare
dummy number;
begin
for i in 1..10000 loop
dummy := test_.by_query(5);
end loop;
end;
0.094 sec.
declare
dummy number;
begin
for i in 1..10000 loop
dummy := test_.by_plsql(5);
end loop;
end;
0.094 sec.
You'll say I am cheater and using deterministic keyword but here it is obvious and is needed by logic. If I remove it, the same scripts are working 1.7 sec vs 1.3 sec, so procedure is only a bit faster, there is no even double-win in performance. The totally opposite effect you will get if you use the function in a query so it is a fair trade.
Sum is reserved word in sql. Change variable name like
declare
n number;
temp number;
sum_ number := 1;
begin
n := &n;
temp := n;
while temp>0 loop
sum_ := temp*sum_;
temp := temp-1;
end loop;
dbms_output.put_line('Factorial of '||n||' is '||sum_);
end;
/
declare
n number;
i number;
sum_of_log_10s number;
exponent number;
base number;
begin
n := &n;
i := 1;
sum_of_log_10s := 0;
while i <= n loop
-- do stuff
sum_of_log_10s := sum_of_log_10s + log(10,i);
i := i + 1;
end loop;
dbms_output.put_line('sum of logs = '||sum_of_log_10s);
exponent := floor(sum_of_log_10s);
base := power(10,sum_of_log_10s - exponent);
dbms_output.put_line(n||'! = '||base||' x 10^'||exponent);
end;
I came up with this code that I like even better than #smnbbrv's answer. It's a great way to check the speed of a machine. I've been using a variation of this since my Atari 800
ALTER SESSION FORCE PARALLEL DDL PARALLEL 16;
ALTER SESSION FORCE PARALLEL DML PARALLEL 16;
ALTER SESSION FORCE PARALLEL QUERY PARALLEL 16;
with t as (
select /*+materialize*/
rownum i
from dual connect by rownum < 100000 -- put number to calculate n! here
)
,t1 as (
select /*+parallel(t,16)*/ /*+materialize*/
sum(log(10,i)) logsum
from t
)
select
trunc(power(10,(mod(logsum,1))),3) ||' x 10^'||trim(to_char(floor(logsum),'999,999,999,999')) factorial
-- logsum
from t1
;
-- returns 2.824 x 10^456,568
Here is the simple code for finding factorial of number at run time...
declare
-- it gives the final answer after computation
fac number :=1;
-- given number n
-- taking input from user
n number := &1;
-- start block
begin
-- start while loop
while n > 0 loop
-- multiple with n and decrease n's value
fac:=n*fac;
--dbms_output.put(n||'*');
n:=n-1;
end loop;
-- end loop
-- print result of fac
dbms_output.put_line(fac);
-- end the begin block
end;

Resources