Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Kjell.

Asked: May 22, 2001 - 10:43 am UTC

Last updated: February 10, 2006 - 1:26 pm UTC

Version: 8.1.6

Viewed 1000+ times

You Asked

I’m passing binary data in to a PL/SQL procedure (PL/SQL long raw datatype).
The procedure clip out 4 or 8 bytes with UTL_RAW.SUBSTRING.

The binary representation is accordance with IEEE 754 float.

I want to convert 4 bytes to float and 8 bytes to double float.
The float shall be returned in Oracle number datatype.

Can this be done in PL/SQL?
If I want to use Java stored procedures to solve this,
how do I write the Java procedure?


Mor details:
The machine the server is running on use IEEE 754 float.

The spec for the storage of the IEE 754 float is found here:
</code> http://research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html <code>

As you see I’m not familiar with Java or C programming.

I want to pass the binary representation of the float which in pl/sql is stored in raw(4)or raw(8).

Which data types shall be used when passing and returning data?


Here is sketch of what I try to do with Java stored procedures:

Create or replace JAVA SOURCE NAMED ‘’BinToFloat’’ AS
Public class BinToFLOAT{
Public static ???? BinToFloat( ){
?
?
?
Return ????;

}
};
/


--wrapper function

create or replace function BinToFloat(?? in ??) Return NUMBER
as LANGUAGE JAVA NAME ‘bintofloat’()return number’;
/


Thanks for any help


and Tom said...

(Thanks to Mark Piermarini for the Java bit here....)

Ok, here we go. first I generated a test set of data in C:

#include "stdio.h"

void main()
{
float x = 1.123;
double y = 12121;
FILE * output;

output = fopen( "test.dat", "wb" );
fwrite( &x, 1, sizeof(x), output );
fwrite( &y, 1, sizeof(y), output );
fclose(output );
}


then, i loaded that up in the database:

create or replace directory my_files as '/export/home/tkyte'
/

drop table t;

create table t ( x blob );

declare
l_bfile bfile;
l_blob blob;
begin
l_bfile := bfilename( 'MY_FILES', 'test.dat' );
dbms_lob.fileopen( l_bfile );

insert into t values ( empty_blob() ) returning x into l_blob;

dbms_lob.loadfromfile( l_blob, l_bfile, dbms_lob.getlength(l_bfile) );
dbms_lob.fileclose( l_bfile );
end;
/


and then:


ops$tkyte@DEV816> create or replace and compile java source
2 named "RawIEEE" as
3 import oracle.sql.*;
4
5 public class RawIEEE {
6 static public NUMBER rawFloatToNUMBER(RAW r) throws Exception {
7 return new NUMBER(
8 Float.intBitsToFloat(
9 Integer.parseInt(r.stringValue(), 16)
10 )
11 );
12 }
13
14 static public NUMBER rawDoubleToNUMBER(RAW r) throws Exception {
15 return new NUMBER(
16 Double.longBitsToDouble(
17 Long.parseLong(r.stringValue(), 16)
18 )
19 );
20 }
21 }
22 /

Java created.

ops$tkyte@DEV816> show errors
No errors.
ops$tkyte@DEV816>
ops$tkyte@DEV816> create or replace function
2 RAW_FLOAT_TO_NUMBER(p_raw in RAW) return NUMBER
3 as language java
4 name 'RawIEEE.rawFloatToNUMBER(oracle.sql.RAW) return
5 oracle.sql.NUMBER';
6 /

Function created.

ops$tkyte@DEV816>
ops$tkyte@DEV816> create or replace function
2 RAW_DOUBLE_TO_NUMBER(p_raw in RAW) return NUMBER
3 as language java
4 name 'RawIEEE.rawDoubleToNUMBER(oracle.sql.RAW) return
5 oracle.sql.NUMBER';
6 /

Function created.



And now I can simply:

ops$tkyte@DEV816> column rfloat format a8
ops$tkyte@DEV816> column rdouble format a16
ops$tkyte@DEV816>
ops$tkyte@DEV816> select dbms_lob.substr( x, 4, 1 ) rfloat,
2 RAW_FLOAT_TO_NUMBER(dbms_lob.substr( x, 4 , 1)) xfloat,
3 dbms_lob.substr( x, 8, 5 ) rdouble,
4 RAW_DOUBLE_TO_NUMBER(dbms_lob.substr( x, 8, 5)) xdouble
5 from t
6 /

RFLOAT XFLOAT RDOUBLE XDOUBLE
-------- ---------- ---------------- ----------
3F8FBE77 1.123 40C7AC8000000000 12121




Rating

  (6 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

RAW_FLOAT_TO_NUMBER

Kjell Lie, June 01, 2001 - 4:44 am UTC

This is exactly what I was looking for.
The Java part is implemented and tested with success.

(The Raw input to Oracle is coming from Programmable Logical Controller’s via a OPC client.)

Thanks very much
Kjell Lie



Binary Conversion to float

Evgeniy, January 28, 2004 - 3:58 am UTC

Excuse me for mine bad English.
What if negative? < 0 ?

raw(4)='C014DD4C'

i=-1072374452, f=-2.326007

Tom Kyte
January 28, 2004 - 8:42 am UTC

you'd have to figure out the java to accomidate that -- you have the start, now you just need to find a java programmer :)



1

astrid, March 26, 2004 - 6:55 pm UTC

1
Tom Kyte
March 26, 2004 - 7:10 pm UTC

I think it is 55 actually

To Binary

Ik, February 09, 2006 - 4:59 pm UTC

Tom,

This question is on converting number to Binary

2437 becomes 100110000101

and select bin_to_num(1,0,0,1,1,0,0,0,0,1,0,1) from dual
gives it back

My question is, if i have a NUMBER column in a table and i want to store it as binary in the database - how do i do it?

SELECT CAST(num AS RAW(10)) from <table>
*
ERROR at line 1:
ORA-00932: inconsistent datatypes: expected BINARY got NUMBER

Oracle Version is 9iR2.

Thanks,

Tom Kyte
February 10, 2006 - 11:54 am UTC

what do you mean by storing it in "binary"

if you want it stored as 0's and 1's, you'll be storing a string:
</code> http://asktom.oracle.com/~tkyte/hexdec/index.html <code>

If you want to store it as a 4 byte integer - you'll need to fetch it out into a integer, using your program language convert it to a 4 byte raw and insert it back in.

But, you should just sort of leave it as a number? What is your goal behind storing it in "binary" (after you define what you mean by binary in this case)

Ik, February 10, 2006 - 12:50 pm UTC

Tom,

Thanks for the reply.

We were looking at storing historic (numeric) data in few tables our datawarehouse (against which queries are infrequent) in a compressed form.

Objective is to save space - oracle's table compression does not give us the savings we desire.

Just wondering, if data is stored in binary form, whether we would get better compression gains. By compression - i mean having custom C code doing the compression. Binary is all 1s and 0s .. and so was hoping that compression factor would be better.

Could you help give us some pointers?

Thanks,

Tom Kyte
February 10, 2006 - 1:26 pm UTC

I doubt it.

To be stored in "binary", you would be talking about a fixed width 4 or 8 byte integer.

ops$tkyte@ORA10GR2> select avg(vsize(object_id)) from all_objects;
 
AVG(VSIZE(OBJECT_ID))
---------------------
           3.79984842
 

there are 50,000'ish numbers stored - each taking an average of about 3.8 bytes.

It is true that the number 
999999999999999
takes more room than the number
1000000000000000
in oracle, but in general - well, no.  You would not be saving much if anything at all


And you would GREATLY complicate client retrieval/functionality as it would not be in a form they could actually USE in the database!!! (functions, think about doing sum, avg, min, max, whatever - on a datatype that is not an oracle type)


Why isn't segment space compression giving you the savings you desire? (perhaps your desire is "unreasonable" given your data?)

how big is big, how small is small?

Have you tried SORTING the data upon loading in order to maximize compression if that is your goal?  Use your knowlege of the data to sort the data by repetitive fields (to clump them together) 

IEEE 754 conversion to oracle numbers

Narendra talele, February 26, 2008 - 4:27 pm UTC

Here is how I have been doing this.... And I have always been looking for much faster and efficient way of doing it...If there is one please let me know.
>>>>>>>>
FUNCTION Hextofloat(v_hex VARCHAR2) RETURN FLOAT AS
i INTEGER := 1 ;
bit INTEGER :=1;
EXP NUMBER :=0 ;
Bin VARCHAR2(32);
Hex VARCHAR2(8);
num1 FLOAT;
val FLOAT;
rem0 VARCHAR2(2000) := NULL;
rem1 VARCHAR2(2000) := NULL;
BEGIN
-- Calculate the Bin rep' for the IEEE-754 formatted bin number
WHILE i <= 8 LOOP
IF SUBSTR(v_Hex,i,1) = '0' THEN
Bin := Bin || '0000';
ELSIF SUBSTR(v_hex,i,1) = '1' THEN
Bin := Bin || '0001' ;
ELSIF SUBSTR(v_hex,i,1) = '2' THEN
Bin := Bin || '0010' ;
ELSIF SUBSTR(v_hex,i,1) = '3' THEN
Bin := Bin || '0011' ;
ELSIF SUBSTR(v_hex,i,1) = '4' THEN
Bin := Bin || '0100' ;
ELSIF SUBSTR(v_hex,i,1) = '5' THEN
Bin := Bin || '0101' ;
ELSIF SUBSTR(v_hex,i,1) = '6' THEN
Bin := Bin || '0110' ;
ELSIF SUBSTR(v_hex,i,1) = '7' THEN
Bin := Bin || '0111' ;
ELSIF SUBSTR(v_hex,i,1) = '8' THEN
Bin := Bin || '1000' ;
ELSIF SUBSTR(v_hex,i,1) = '9' THEN
Bin := Bin || '1001' ;
ELSIF SUBSTR(v_hex,i,1) = 'A' THEN
Bin := Bin || '1010' ;
ELSIF SUBSTR(v_hex,i,1) = 'B' THEN
Bin := Bin || '1011' ;
ELSIF SUBSTR(v_hex,i,1) = 'C' THEN
Bin := Bin || '1100';
ELSIF SUBSTR(v_hex,i,1) = 'D' THEN
Bin := Bin || '1101' ;
ELSIF SUBSTR(v_hex,i,1) = 'E' THEN
Bin := Bin || '1110' ;
ELSIF SUBSTR(v_hex,i,1) = 'F' THEN
Bin := Bin || '1111' ;
END IF;
i := i + 1 ;
END LOOP;
-- do the sign
IF SUBSTR(Bin,1,1) = '1' THEN
bit := bit * (-1) ;
END IF;
-- do the exponent
rem0 := SUBSTR(Bin,2,8) ;
i:=1 ;
WHILE i <= 8 LOOP
EXP := EXP + TO_NUMBER(SUBSTR(rem0,i,1)) * POWER(2,8-i) ;
i := i + 1 ;
END LOOP ;
EXP := EXP - 127 ;
-- subtract 127 per IEEE 754 std.
-- do the mantissa
rem1 := SUBSTR(Bin,10,23) ;
-- add the '1' as per IEEE 754.
i:=1 ;
num1 := 1 ;
WHILE i <= 23 LOOP
num1 := num1 + TO_NUMBER(SUBSTR(rem1,i,1)) * POWER(2,(-i)) ;
i := i + 1 ;
END LOOP ;
val := (bit) * (num1) * POWER(2,EXP) ;
--DBMS_OUTPUT.PUT_LINE ( 'Decimal number is ' || val );
IF v_hex = '00000000' THEN
RETURN 0 ;
ELSE
RETURN val;
END IF;
EXCEPTION
WHEN OTHERS THEN
-- DBMS_OUTPUT.PUT_LINE ( 'In func others= ' || sqlcode || sqlerrm );
NULL;
END Hextofloat;
>>>>>>>>>>>>>>>>>>>>
Here is how we can call it...
select mdnorm.Hextofloat('3F8FBE77') from dual ;
1.12300002574921
>>>>>>>>>>>>>>>>>>>>

More to Explore

CLOB/BLOB/etc

Complete documentation on Securefiles and Large Objects here