Figuring out Float64 representation for Newbies

Ever want to know how Float64 numbers are represented in the raw machine bits?

I have written a julia code to show it to you.


function DescribeFloat64(x::Float64;verbose=false)
    # Local function convert binary string 2 Int64
    cbs2Int64(s) = parse(Int64,"0b" * s)

    # Local function vprintln
    function vprintln(args...)
        if verbose == true
            println(args...)
        end
    end

    # Local ALTERNATIVE significand function
    function significand_alt(T::DataType,manArr::Array{Int64,1})
        # Check if all the bits in MantissaArray is 0
        if sum(manArr) == 0
            return convert(T,0)
        end
        setprecision(BigFloat, 52) do
            acc = BigFloat(0.0)
            multiplier = BigFloat(0.5)
            for k in manArr
                acc += k == 1 ? multiplier : BigFloat(0.0)
                multiplier /= BigFloat(2.0)
            end
            acc += BigFloat(1.0) 
            return convert(Float64,acc)
        end
    end

    # Local variables used in this function #
    local expoBiasValue = 1023
    local specialflags = false # Boolean flag
    local bs = bitstring(x)  # Character bit string
    local signbit = bs[1] == '1' ? 1 : 0 # Sign Bit Int64
    local expoView = @view bs[2:12] # a View of bit stream relevant to exponent
    # exponent Array{Int64,1}
    local expoArray = [ k == '1' ? 1 : 0 for k in expoView ]
    local expo  = cbs2Int64(expoView) # exponent Int64
    local mantissaView = @view bs[13:64] # a View of bit stream relevant to mantissa
    # mantissa Array{Int64,1}
    local mantissaArray = Int64[ k == '1' ? 1 : 0 for k in mantissaView ] 
    local mysignificand = string(significand(x)) # significand Float64
    # Strip the negative sign if second character is a number digit
    if mysignificand[1] == '-' &&  mysignificand[2] == '1' || mysignificand[2] == '0'
        mysignificand = lstrip(mysignificand,'-')
    end

    # List of special Boolean flags
    local INF_flag = false # Boolean flag
    local NaN_flag = false # Boolean flag
    local subnormal_flag = false
    # End of local variables

    println("------------------------------------------------------------")
    println("Float64 value : ",x)
    println("Raw Bitstring : ",bs,"₂")
    println("======================")
    println("Sign Bit : ",bs[1],"₂")
    println("Sign Value : ",signbit)

    println("======================")
    vprintln("Expo Array is ",expoArray)
    println("Exponent Bits : ",expoView,"₂")  
    println("Exponent raw value : ",expo)
    println("Exponent normalised value : ",expo," - $(expoBiasValue) = ",expo - expoBiasValue)

    println("======================")
    vprintln("Typeof mantissa is ",typeof(mantissaArray))
    vprintln("mantissa Array is ",mantissaArray)
    println("Mantissa : I.",bs[13:64],"₂")
    println("Significand : ",mysignificand)
 
    println("======================")
    ##############################################
    # Now check ALL conditions for special flags #
    ##############################################

    # Step 1: Checking for INF
    #     If all bits in mantissa are ZERO 
    #        and all bits in exponent are ONE
    #     then INF_flag is true
    if sum(mantissaArray) == 0 && prod(expoArray) == 1
        specialflags = true
        INF_flag = true
    end

    # Step 2: Checking for NaN
    #     If some bits in mantissa are NOT ZERO 
    #        and all bits in exponent are ONE
    #     then NaN_flag is true
    if sum(mantissaArray) > 0 && prod(expoArray) == 1
        specialflags = true
        NaN_flag = true
    end

    # Step 2: Checking for subnormal
    #     If some bits in mantissa are NOT ZERO 
    #        and all bits in exponent are ZERO
    #     then subnormal_flag is true
    if sum(mantissaArray) > 0 && sum(expoArray) == 0
        specialflags = true
        subnormal_flag = true
    end

    print("Value is ")

    if specialflags == false
      println("(-1)^",signbit," * ",mysignificand," * 2^",expo - 1023)
    else # specialflags == true
        if INF_flag
            print(signbit == 1 ? "Negative " : "Positive ")
            println("INF")
        end
        if NaN_flag
            println("NaN")
        end
        if subnormal_flag
            println("subnormal")
        end
    end

    println("Value is ",x)
    println("------------------------------------------------------------")
end

println("Testing 1.5"); DescribeFloat64(1.5); print()

println("Testing -1.5"); DescribeFloat64(-1.5); print()
println("Testing 1.1"); DescribeFloat64(1.1); print()
println("Testing -1.1"); DescribeFloat64(-1.1); print()
println("Testing 6.02214076e23"); DescribeFloat64(6.02214076e23); print()
println("Testing 1.0/0.0 == +INF"); DescribeFloat64(1.0/0.0); print()
println("Testing -1.0/-0.0 == +INF"); DescribeFloat64(-1.0/-0.0); print()
println("Testing -1.0/0.0 == -INF"); DescribeFloat64(-1.0/0.0); print()
println("Testing 1.0/-0.0 == -INF"); DescribeFloat64(1.0/-0.0); print()
println("Testing 0.0/0.0 == NaN"); DescribeFloat64(0.0/0.0); print()
println("Testing 0.0 == normal Zero"); DescribeFloat64(0.0); print()
println("Testing -0.0 == sign Zero"); DescribeFloat64(-0.0); print()
println("Testing 5.0e-324 == subnormal"); DescribeFloat64(5.0e-324); print()
println("Testing floatmin(Float64) == 2.2250738585072014e-308"); DescribeFloat64(2.2250738585072014e-308); print()

Here is a sample usage

julia> DescribeFloat64(-123.456789)
------------------------------------------------------------
Float64 value : -123.456789
Raw Bitstring : 1100000001011110110111010011110000000111111011100000101100001011₂
======================
Sign Bit : 1₂
Sign Value : 1
======================
Exponent Bits : 10000000101₂
Exponent raw value : 1029
Exponent normalised value : 1029 - 1023 = 6
======================
Mantissa : I.1110110111010011110000000111111011100000101100001011₂
Significand : 1.929012328125
======================
Value is (-1)^1 * 1.929012328125 * 2^6
Value is -123.456789
------------------------------------------------------------

Enjoy.

3 Likes

You might also be interested GitHub - tecosaur/About.jl: Mirror of https://code.tecosaur.net/tec/About.jl

1 Like

Now you can answer questions like these

julia> 1.1e14 - (1.1 * 10.0^14)
-0.015625

julia> DescribeFloat64(1.1e14)
------------------------------------------------------------
Float64 value : 1.1e14
Raw Bitstring : 0100001011011001000000101101011110111011001110000000000000000000₂
======================
Sign Bit : 0₂
Sign Value : 0
======================
Exponent Bits : 10000101101₂
Exponent raw value : 1069
Exponent normalised value : 1069 - 1023 = 46
======================
Mantissa : I.1001000000101101011110111011001110000000000000000000₂
Significand : 1.5631940186722204
======================
Value is (-1)^0 * 1.5631940186722204 * 2^46
Value is 1.1e14
------------------------------------------------------------

julia> DescribeFloat64(1.1 * 10.0^14)
------------------------------------------------------------
Float64 value : 1.1000000000000002e14
Raw Bitstring : 0100001011011001000000101101011110111011001110000000000000000001₂
======================
Sign Bit : 0₂
Sign Value : 0
======================
Exponent Bits : 10000101101₂
Exponent raw value : 1069
Exponent normalised value : 1069 - 1023 = 46
======================
Mantissa : I.1001000000101101011110111011001110000000000000000001₂
Significand : 1.5631940186722206
======================
Value is (-1)^0 * 1.5631940186722206 * 2^46
Value is 1.1000000000000002e14
------------------------------------------------------------