How to save and restore a Float64 value to disk

n = sqrt(2.0)

"""
How do I save my Float64 value in variable n
to disk in such a way that I can

1. restore back to another variable y
2. such that y is Float64 and has the exact value as n
3. This restoration will work for FUTURE julia version 1.4, 1.5, 1.6, 1.7, 1.8, 1.9
4. Without using any packages. ie I'm not at the mercy of any packages
"""

And by exactness, I mean bit to bit exactness.

julia> n = sqrt(2.0)
1.4142135623730951

julia> write("saved_value", n)
8

julia> y = read("saved_value", Float64)
1.4142135623730951

julia> y == n
true

I am not sure why this is a requirement — you can always pin the exact version of the package you are using in your project, and it will work forever.

The solution above suggested by @GunnarFarneback will work on architectures with the same endianness. Of course in practice you may not encounter a big-endian machine, but when you do, this method will silently corrupt the value.

2 Likes
$ hexedit saved_value
00000000   CD 3B 7F 66  9E A0 F6 3F                            .;.f...?
00000010

Well, looks like this thing will only work if the endingness of the physical machine is guarantee. Is there a way to store what the ending of the saving machine is so that the restoration can swap the byte order if the endingness is different?

n=sqrt(2.0)
s=string(n)
write("saved_value", s)
s_in=read("saved_value",String)
y=parse(Float64,s_in)
y===n

:thinking: :rofl: :wink:

1 Like

I am assuming you mean endianness.

Yes, of course, you can just save it as a character flag or something. You are well on your way to developing your own storage format. And this is usually not a good idea. :wink:

You can use whatever convention you like to document the endianness in the file. A common trick used in file formats is to store a chosen UInt16 value in the header, then upon reading detect whether it reads as expected or byte-swapped.

Alternatively you just decide that you will always store in little endian format and byteswap as needed on the architecture you’re running on.

julia> n = sqrt(2.0)
1.4142135623730951

julia> write("saved_value", htol(n))
8

julia> y = ltoh(read("saved_value", Float64))
1.4142135623730951

For a solution that is text based, unambiguous and valid across systems and languages:

using Printf

julia> n = sqrt(2.0)
1.4142135623730951

julia> @printf("%a", n)
0x1.6a09e667f3bcdp+0

julia> parse(Float64, "0x1.6a09e667f3bcdp+0") == n
true
3 Likes

I better do a write up to summarise all four methods

using Printf

function SavingFloat_method01(filename,x)
    write(filename, x)
end

function RestoringFloat_method01(filename)
    read(filename,Float64)
end

function SavingFloat_method02(filename,x)
    write(filename, string(x))
end

function RestoringFloat_method02(filename)
    parse(Float64,read(filename,String))
end

function SavingFloat_method03(filename,x)
    write(filename, @sprintf("%a",x))
end

function RestoringFloat_method03(filename)
    parse(Float64,read(filename,String))
end

function SavingFloat_method04(filename,x)
    write(filename, htol(x))
end

function RestoringFloat_method04(filename)
    ltoh(read(filename,Float64))
end

function read_rawbytes(filename)
    IO = open(filename,"r")
    buff = UInt8[]
    while !eof(IO)
        push!(buff,read(IO,UInt8))
    end
    buff
end


""" Value to be saved """
n = sqrt(2.0)

""" Framework for testing """

function framework_test(save_func,restore_func,version)
    save_func("SavedValue_method$(version)",n)
    y = undef
    y = restore_func("SavedValue_method$(version)")
    println("Method$(version):   y === n is ",y === n,"    y=",y," n=",n)
    rawbytes = read_rawbytes("SavedValue_method$(version)")
    # println("typeof(rawbytes) is ",typeof(rawbytes))
    print("rawbytes: ")
    for k in rawbytes
        print(string(k,base=16)," ")
    end
    println()
end

"""
     Testing method 01
"""

framework_test(SavingFloat_method01,
               RestoringFloat_method01,
               "01")

"""
    Testing method 02
"""

framework_test(SavingFloat_method02,
               RestoringFloat_method02,
               "02")

"""
   Testing method 03
"""

framework_test(SavingFloat_method03,
               RestoringFloat_method03,
               "03")

"""
   Testing method 04
"""

framework_test(SavingFloat_method04,
               RestoringFloat_method04,
               "04")

The output is

Method01:   y === n is true    y=1.4142135623730951 n=1.4142135623730951
rawbytes: cd 3b 7f 66 9e a0 f6 3f 
Method02:   y === n is true    y=1.4142135623730951 n=1.4142135623730951
rawbytes: 31 2e 34 31 34 32 31 33 35 36 32 33 37 33 30 39 35 31 
Method03:   y === n is true    y=1.4142135623730951 n=1.4142135623730951
rawbytes: 30 78 31 2e 36 61 30 39 65 36 36 37 66 33 62 63 64 70 2b 30 
Method04:   y === n is true    y=1.4142135623730951 n=1.4142135623730951
rawbytes: cd 3b 7f 66 9e a0 f6 3f