序列化或表示BigFloat很棘手,因为:
a. BigFloat结构使用指针,
b. 指向的缓冲区由GMP库控制,该库是Julia中包装的外部库。
因此,首先需要一些函数以准确且易于操作的方式对BigFloat进行文本格式化:
function numbits(a::BigFloat)
n = a.prec
for i=1:(a.prec>>count_ones(sizeof(Base.GMP.Limb)*8-1))
tz = trailing_zeros(unsafe_load(a.d,i))
n -= tz
if tz < sizeof(Base.GMP.Limb)*8
break
end
end
return n
end
mantissarep(a::BigFloat) = (BigInt(a*BigFloat(2)^(numbits(a)-a.exp)),
numbits(a)-a.exp)
function bigfloattext(bf::BigFloat)
(a,b) = mantissarep(bf)
return "$(sign(a)==-1 ? "-" : "")big\"$(dec(abs(a)))\"/big\"2\"^$(b)"
end
function bigfloatbintext(bf::BigFloat)
(a,b) = mantissarep(bf)
return "$(sign(a)==-1 ? "-" : "")big\"0b$(bin(abs(a)))\"/big\"2\"^0b$(bin(b))"
end
使用这些函数,将BigFloat序列化的一种方法是简单地编写
bigfloattext
的字符串输出。这种方法不是非常紧凑,但它将很容易反序列化(可以使用BigFloat的
parse
方法),并且相对清晰。为了使序列化更加紧凑,可以编写
mantissarep
的字节和BigFloat的精度字段。如果需要,这会使答案略长,所以我会添加。例如编写:
julia> a = sin(BigFloat(π)/3)
8.660254037844386467637231707529361834714026269051903140279034897259665084543988e-01
julia> println(bigfloattext(a))
big"100278890836790510567389408543623384672710501789331344711007167057270294106993"/big"2"^256
julia>
julia> big"100278890836790510567389408543623384672710501789331344711007167057270294106993"/big"2"^256 == a
true
注意(关于其他答案):写入类型为Ptr {Limb}的BigFloat的d字段(=数据字段)是相当无用的,可能会导致内存损坏。
更新:在发布者的评论指导下,这里提供另外两个将BigFloat转换为字节表示的函数:
function BigFloat2Bytes(bf::BigFloat)
bf2 = bf/big"2"^exponent(bf)-1.0
bvec = Vector{UInt8}()
push!(bvec,0x01)
while bf2 != 0.0
bf2 *= 256
b = trunc(Int, bf2)
push!(bvec, UInt8(b))
bf2 -= b
end
return (bvec, exponent(bf))
end
function Bytes2BigFloat(bvec, e)
bf = zero(BigInt)
for b in bvec
bf = bf*256 + b
end
return BigFloat(bf*BigFloat(2)^(e-8*length(bvec)+8))
end
以下是我们拥有的内容:
julia> a = BigFloat(123.2323)
1.23232299999999995065991242881...e+02
julia> BigFloat2Bytes(a)
(UInt8[0x01, 0xec, 0xed, 0xe0, 0x0d, 0x1b, 0x71, 0x70], 6)
julia> Bytes2BigFloat(BigFloat2Bytes(a)...)
1.23232299999999995065991242881...e+02
julia> Bytes2BigFloat(BigFloat2Bytes(a)...) == a
true
numbits
返回的是二进制位数(而不是十进制)。 - Dan Getz