2012年4月11日 星期三

Transact-SQL IEEE 754 轉換程式 -- 32 bits (Convert Decimal To IEEE 754 Binary Floating-Point)



此 Stored Procedure 可將輸入的十進制數字轉換為 IEEE 754 標準二進制浮點格式 (32 bits) 輸出。

IEEE 754 共分為 S (Sign)、E (Exponent)、M (Mantisa) 三個部分 -




計算方式: 以 -60.25 為例

1) -60.25 為負數,將 S 設定為 1 (若為正數則設定為 0)。

2) 將 60.25 轉換為二進制浮點數 111100.01

3) 將此二進制浮點數正規劃 1.1110001 * 2^5

4) 將 5 (2 的乘方) 加上 127 後,轉換為二進制 10000100,存放於 E。

5) 將 1.1110001 去掉最前面的 "1." 後,存放於 M (共 32 bits,不足補 0);得到 11100010000000000000000 。

6) 最後得到 1 10000100 11100010000000000000000


程式碼

if object_id ('usp_IEEE754_Generate') is not null
 drop proc usp_IEEE754_Generate
go


create proc usp_IEEE754_Generate @prm_num                     float, 
                                 @rtn_IEEE_754_string         varchar(32) output,
                                 @rtn_IEEE_754_binary_codes   binary(32)  output
/*----------------------------------------------------------------------------------+
 | Author: Wei-jie Yang                                                             |
 | Date:   2012/05/10                                                               |
 | All Rights Reserved                                                              |
 |                                                                                  |
 | [Description]                                                                    |
 |  This stored procedure is used to convert decimal floating number into binary    |
 |  floating number according to IEEE 754 (Standard for Binary Floating-Point       |
 |  Arithmetic).                                                                    |
 |                                                                                  |
 | [Parameters]                                                                     |
 |  (1).INPUT Parameter                                                             |
 |      - @prm_num: The number is going to be converted.                            |
 |  (2).OUTPUT Parameter                                                            |
 |      - @rtn_IEEE_754_string: Converted number stored in varchar data type        |
 |      - @rtn_IEEE_754_codes : Converted number stored in binary data type         |
 |                                                                                  |
 | [Usage]                                                                          |
 |  declare @return_1 varchar(32), @return_2 binary(32)                             |
 |  exec usp_IEEE754_Generate -0.75, @return_1 output, @return_2 output             |
 |  select @return_1 as Varchar_Mode                                                |
 |  select @return_2 as Binary_Mode                                                 |
 +----------------------------------------------------------------------------------*/
as

declare @dot_position            int,

        @exp_part_0_1            varchar(8),

        @fraction_part           float,
        @fraction_part_0_1       varchar(23),

        @i                       int,
        @integer_fraction_0_1    varchar(100),
        @integer_part            int,
        @integer_part_0_1        varchar(23),
        @j                       int,
        @k                       int,

        @leading_one_position    int,

        @sign                    char(1), -- 0: Zero, Positive Number   1: Negative Number
        @single_digit            char(1),

        @temp_exp                int,
        @temp_fraction           float,
        @temp_integer            int  

declare @integer_result table (
        i_sn                     int,
        i_digit                  char(1) 
        )

declare @exp_result table (
        e_sn                     int,
        e_digit                  char(1) 
        )

set nocount on


-- Initialization ---------------------------------------------------
if @prm_num = 0
 goto Zero_Handler

if @prm_num > 0
 begin
    select @sign = '0'
 end
else
 begin 
    select @sign = '1'
    select @prm_num = @prm_num * -1
 end

select @integer_part  = @prm_num / 1
select @fraction_part = @prm_num - @integer_part
---------------------------------------------------------------------


-- Integer Part -----------------------------------------------------
select @temp_integer = @integer_part
select @i = 1

while @temp_integer > 1
 begin
    select @single_digit = @temp_integer % 2
    select @temp_integer = @temp_integer / 2
 
    insert into @integer_result values(@i, @single_digit)
 
    select @i = @i + 1 
 end

insert into @integer_result values(@i, @temp_integer)

select @integer_part_0_1 = ''

while @i > 0
 begin
    select @single_digit = i_digit
      from @integer_result
     where i_sn = @i

    select @integer_part_0_1 = @integer_part_0_1 + @single_digit

    select @i = @i - 1
 end
---------------------------------------------------------------------


-- Fraction Part ----------------------------------------------------
select @temp_fraction     = @fraction_part
select @j                 = 1
select @fraction_part_0_1 = ''

if @temp_fraction > 0
 begin
    while @temp_fraction > 0
     begin
        select @temp_fraction = @temp_fraction * 2
  
        if @temp_fraction >= 1
         begin
            select @single_digit = 1
            select @temp_fraction = @temp_fraction - 1
         end
        else
         begin
            select @single_digit = 0
         end
   
        select @fraction_part_0_1 = @fraction_part_0_1 + @single_digit
     end
 end
else
 begin
    select @fraction_part_0_1 = '0'
 end
---------------------------------------------------------------------


select @integer_fraction_0_1 = @integer_part_0_1 + '.' + @fraction_part_0_1
select @leading_one_position = charindex('1', @integer_fraction_0_1)
select @dot_position         = charindex('.', @integer_fraction_0_1)

if @dot_position < @leading_one_position
   select @temp_exp = @dot_position - @leading_one_position + 127
else
   select @temp_exp = @dot_position - @leading_one_position - 1 + 127

select @integer_fraction_0_1 = replace(@integer_fraction_0_1, '.', '')

select @integer_fraction_0_1 = replace(ltrim(replace(@integer_fraction_0_1, '0', ' ')), ' ', '0')   -- remove leading zeros
select @integer_fraction_0_1 = substring(@integer_fraction_0_1, 2, len(@integer_fraction_0_1))  -- remove the first '1'


-- Exponential Part -------------------------------------------------
select @k = 1

while @temp_exp > 1
 begin
    select @single_digit = @temp_exp % 2
    select @temp_exp     = @temp_exp / 2
 
    insert into @exp_result values(@k, @single_digit)
 
    select @k = @k + 1 
 end

insert into @exp_result values(@k, '1')

select @exp_part_0_1 = ''

while @k > 0
 begin
    select @single_digit = e_digit
      from @exp_result
     where e_sn = @k

    select @exp_part_0_1 = @exp_part_0_1 + @single_digit

    select @k = @k - 1
 end
 
select @exp_part_0_1 = right('00000000' + @exp_part_0_1, 8)
---------------------------------------------------------------------


-- IEEE 754 Format --------------------------------------------------
select @rtn_IEEE_754_string = left(convert(varchar(32), @sign + @exp_part_0_1 + @integer_fraction_0_1, 2) + '00000000000000000000000000000000', 32)
select @rtn_IEEE_754_binary_codes = convert(binary(32), @rtn_IEEE_754_string, 2)
---------------------------------------------------------------------

set nocount off
return


Zero_Handler:
 select @rtn_IEEE_754_string = '00000000000000000000000000000000'
 select @rtn_IEEE_754_binary_codes = convert(varbinary(64), @rtn_IEEE_754_string, 2)

 set nocount off
 return