Is there a direct way to get binary representation of a float?

2,971 views
Skip to first unread message

Uliano Guerrini

unread,
Oct 17, 2015, 12:04:14 PM10/17/15
to julia-users
I don't see the point of passing through a string like this:

julia> hex2bytes(num2hex(1.0))

8-element Array{UInt8,1}:

 0x3f

 0xf0

 0x00

 0x00

 0x00

 0x00

 0x00

 0x00 

I assume that the intermediate passage slows down the conversion.

Same question for the way back from binary to float.


Kristoffer Carlsson

unread,
Oct 17, 2015, 12:19:31 PM10/17/15
to julia-users
julia> a = [1.0]
1-element Array{Float64,1}:
 1.0

julia> b = reinterpret(UInt8, a)
8-element Array{UInt8,1}:
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0xf0
 0x3f

julia> c = reinterpret(Float64, b)
1-element Array{Float64,1}:
 1.0

Marcus Appelros

unread,
Oct 17, 2015, 12:27:23 PM10/17/15
to julia-users
Also possible with the bits function:
julia> bits(1.0)
"0011111111110000000000000000000000000000000000000000000000000000"

Jeffrey Sarnoff

unread,
Oct 17, 2015, 12:53:41 PM10/17/15
to julia-users
other binary representations of a Float64

julia> exp1_inStdHexFormat = @sprintf("%a",exp(1.0))
"0x1.5bf0a8b145769p+1"

julia> parse(Float64,exp1_inStdHexFormat)
2.718281828459045

and from my satchel

exp1_as_float = exp(1.0)
2.718281828459045

exp1_as_unsigned = reinterpret(Uint64, exp1_as_float)
0x4005bf0a8b145769

exp1 = reinterpret(Float64, exp1_as_unsigned)
2.718281828459045

Uliano Guerrini

unread,
Oct 18, 2015, 1:42:57 AM10/18/15
to julia-users
Thanks to the pointers given I dug a little bit under the hood ad found the promising unbox intrinsic function. It would exactly be what I was looking for but it seems to work only one way:

julia> using Base.Intrinsics


julia> theFloat=1.0

1.0


julia> b=unbox(Int64,theFloat)

4607182418800017408


julia> bits(theFloat)

"0011111111110000000000000000000000000000000000000000000000000000"


julia> type Words2x32

         lo::Int32

         hi::Int32

       end


julia> c=Words2x32(0,1072693248)

Words2x32(0,1072693248)


julia> d=unbox(Float64,c)

1.0


julia> bits(d)

"0011111111110000000000000000000000000000000000000000000000000000"


julia> e=unbox(Words2x32,d)

Segmentation fault: 11


Is it a bug or I have used the function in an unexpected way?




ele...@gmail.com

unread,
Oct 18, 2015, 5:28:17 PM10/18/15
to julia-users
You can't unbox an already unboxed object.

Cheers
Lex 

Uliano Guerrini

unread,
Oct 19, 2015, 12:59:29 AM10/19/15
to julia...@googlegroups.com

> Il giorno 18 ott 2015, alle ore 23:28, ele...@gmail.com ha scritto:
>
>
> You can't unbox an already unboxed object.
>
> Cheers
> Lex

I see, however my question is if there is a way to perform the conversion shown (or the like) without any (or minimal) run time penalty that is the reason why when I found that reinterpret was calling intrinsics I focussed on them. In C time penalty is 0 If Julia proposes itself as solving the two language problem I should not think to write parts of the code in C

Gunnar Farnebäck

unread,
Oct 19, 2015, 2:56:32 AM10/19/15
to julia-users
It might help if you explain something about what you want to do with the binary representation.

For example,

julia> f(x::Float64) = reinterpret(Float64, reinterpret(UInt64, x) & 0x7fffffffffffffff)
f (generic function with 1 method)

generates very simple LLVM code

julia> code_llvm(f, (Float64,))

define double @julia_f_21398(double) {
top:  
  %1 = bitcast double %0 to i64
  %2 = and i64 %1, 9223372036854775807
  %3 = bitcast i64 %2 to double
  ret double %3
}

which is compiled by LLVM into

julia> code_native(f, (Float64,))
        .text
Filename: none
Source line: 0
        pushq   %rbp
        movq    %rsp, %rbp
Source line: 1
        vmovq   %xmm0, %rax
        movabsq $9223372036854775807, %rcx # imm = 0x7FFFFFFFFFFFFFFF
        andq    %rax, %rcx
        vmovq   %rcx, %xmm0
        popq    %rbp
        retq
        nopl    (%rax)
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $16, %rsp
        movq    (%rsi), %rax
        vmovsd  (%rax), %xmm0
        movabsq $f, %rax
        callq   *%rax
        vmovsd  %xmm0, -8(%rbp)
        movabsq $__pool_alloc, %rax
        callq   *%rax
        movabsq $140629191800336, %rcx  # imm = 0x7FE6C905B610
        movq    %rcx, -8(%rax)
        vmovsd  -8(%rbp), %xmm0
        vmovsd  %xmm0, (%rax)
        addq    $16, %rsp
        popq    %rbp
        retq

The lines up to the first retq look reasonable to me but I have no idea what's up with the rest of the output. Possibly a bug in code_native.

Uliano Guerrini

unread,
Oct 19, 2015, 4:28:15 AM10/19/15
to julia-users


Il giorno lunedì 19 ottobre 2015 08:56:32 UTC+2, Gunnar Farnebäck ha scritto:
It might help if you explain something about what you want to do with the binary representation.

I would like to have an efficient way to access bytes, 16 and 32 bit words of larger types (in this case 8 bytes, not necessarily floating point). Possibly as efficient and as easy as it could be in C/C++

    struct Words32 {

        int32_t lo;

        int32_t hi;

    };

    union Both {

        struct Words32 w;

        int64_t i;

    };

    union Both b;

    b.i=0x7fffffff3fffffff;

    printf("lo:0x%08x\nhi:0x%08x\n",b.w.lo,b.w.hi);


gives

lo:0x3fffffff

hi:0x7fffffff


type Words32

  hi::Int32

  lo::Int32

end

a=0x7fffffff3fffffff

reinterpret(Words32,a)


gives


ERROR: reinterpret: expected bits type as first argument

 in reinterpret at essentials.jl:115


 

For example,

julia> f(x::Float64) = reinterpret(Float64, reinterpret(UInt64, x) & 0x7fffffffffffffff)
f (generic function with 1 method)


I have seen that I can reinterpret a bits type into another bits type of the SAME size. unfortunately my Words32 isn't, even if it size is completely specified and should be known ahead of time by the compiler. I'm looking for ways to access its smaller chunks without passing through a conversion into an hexadecimal string.

Gunnar Farnebäck

unread,
Oct 19, 2015, 5:29:36 AM10/19/15
to julia-users
 
Is this good enough?

julia> immutable Words32

         hi::Int32
         lo::Int32
       end

julia> a = 0x7fffffff3fffffff
0x7fffffff3fffffff

julia> reinterpret(Words32, [a])
1-element Array{Words32,1}:
 Words32(1073741823,2147483647)

Uliano Guerrini

unread,
Oct 19, 2015, 6:17:35 AM10/19/15
to julia-users


Il giorno lunedì 19 ottobre 2015 11:29:36 UTC+2, Gunnar Farnebäck ha scritto:

 
Is this good enough?

julia> immutable Words32
         hi::Int32
         lo::Int32
       end

julia> a = 0x7fffffff3fffffff
0x7fffffff3fffffff

julia> reinterpret(Words32, [a])
1-element Array{Words32,1}:
 Words32(1073741823,2147483647)

It seems to me (I have no expertise to evaluate llvm or machine code) that there is a considerable overload in getting the array of a and then dereferencing the array of what I get. In my example C/C++ does all this ahead of time, I'm not sure that Julia would. 

Stefan Karpinski

unread,
Oct 19, 2015, 6:54:34 AM10/19/15
to Julia Users
Here's a simple function that gives the UInt32 words of a Float64:

function words(x::Float64)
    y = reinterpret(UInt64, x)
    y % UInt32, (y >> 32) % UInt32
end

julia> words(1.23)
(0x7ae147ae,0x3ff3ae14)

julia> @code_native words(1.23)
.section __TEXT,__text,regular,pure_instructions
Filename: none
Source line: 2
Source line: [inline] essentials.jl:119
pushq %rbp
movq %rsp, %rbp
Source line: 2
Source line: [inline] essentials.jl:119
movd %xmm0, %rax
Source line: 3
Source line: [inline] int.jl:177
movq %rax, %rdx
shrq $32, %rdx
popq %rbp
ret

That's about as efficient as the code for this is going to get. Moreover if you use it to just access to high or low word it will get inlined and much of the code eliminated:

lo(x::Float64) = words(x)[1]

julia> lo(1.23)
0x7ae147ae

julia> @code_native lo(1.23)
.section __TEXT,__text,regular,pure_instructions
Filename: none
Source line: 2
Source line: [inline] essentials.jl:119
pushq %rbp
movq %rsp, %rbp
Source line: 2
Source line: [inline] essentials.jl:119
movd %xmm0, %rax
Source line: 3
Source line: [inline] tuple.jl:8
popq %rbp
ret

Uliano Guerrini

unread,
Oct 19, 2015, 7:38:33 AM10/19/15
to julia-users


Il giorno lunedì 19 ottobre 2015 12:54:34 UTC+2, Stefan Karpinski ha scritto:
Here's a simple function that gives the UInt32 words of a Float64:

function words(x::Float64)
    y = reinterpret(UInt64, x)
    y % UInt32, (y >> 32) % UInt32
end

Nice!

This is 1 machine op (the shift) from C version, I guess that going on par would be (very) low priority for the time being. 

Thanks

Uliano
Reply all
Reply to author
Forward
0 new messages