我想获得BigFloat
个数字的二进制数字。我认为最简单的方法是将数字转换为字节数组。在this question中有一种方法可以为Float64
执行此操作,但是当我为BigFloat
尝试相同时,它会告诉我,write没有BigFloat
的方法。< / p>
我怎么能和朱莉娅一起做?
PS。我知道有一种方法可以将二进制数字作为字符串,但这会带来巨大的开销,我想避免它。
答案 0 :(得分:3)
序列化或表示BigFloats很棘手,因为: a。 BigFloat结构使用指针, b。指向缓冲区由GMP库控制,这是一个外部的图书馆包裹在朱莉娅。
所以作为第一步,这里有一些函数以一种准确且易于操作的方式对BigFloat进行文本格式化:
# numbits(a) returns the number of significant digits in a BigFloat
# including digits both left and right of floating point.
# For example:
# julia> numbits(BigFloat(3/8)
# 2
#
# This is the trickiest function since it looks slightly into
# the internal representation in GMP of BigInt data.
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) returns a tuple with two elements. The first element is a BigInt
# holding all the significant bits of a BigFloat, and the second holds
# the exponent needed to return the floating point to its position.
# Thus, as an example, the following holds:
# julia> a = BigFloat(1.1)
# 1.100000000000000088817841...
#
# julia> mantissarep(a)
# (2476979795053773, 51)
#
# julia> big"2476979795053773"/big"2"^51 == a
# true
#
mantissarep(a::BigFloat) = (BigInt(a*BigFloat(2)^(numbits(a)-a.exp)),
numbits(a)-a.exp)
# bigfloattext(a) returns an easy textual representation of a BigFloat
function bigfloattext(bf::BigFloat)
(a,b) = mantissarep(bf)
return "$(sign(a)==-1 ? "-" : "")big\"$(dec(abs(a)))\"/big\"2\"^$(b)"
end
# bigfloatbintext(a) returns an easy binary textual representation a BigFloat
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
的字符串输出。这种方法不是很紧凑,但很容易反序列化(可以使用BigFloats的parse
方法)并且相对清晰。为了实现更紧凑的序列化,可以写入mantissarep
的字节以及BigFloat的精度字段。这会使这个答案更长一点,所以如果要求我会添加。
例如,写作:
julia> a = sin(BigFloat(π)/3) # calculate some BigFloat
8.660254037844386467637231707529361834714026269051903140279034897259665084543988e-01
julia> println(bigfloattext(a)) # represent it as a string
big"100278890836790510567389408543623384672710501789331344711007167057270294106993"/big"2"^256
julia> # show representation is faithful
julia> big"100278890836790510567389408543623384672710501789331344711007167057270294106993"/big"2"^256 == a
true
请注意(关于其他答案)编写BigFloat的d
字段(=数据字段)是一个Ptr {Limb}类型相当无用且可能导致内存损坏< / em>的
更新:在下面的海报评论的指导下,这里有另外几个将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
答案 1 :(得分:1)
我试图使用其他方法。我不确定我做得对吗!但也许它很有趣。 :)
我们可能通过使用负责BigFloat(MPFR)的库来获得最小的开销。所以我试图调用mpfr_sprintf(http://www.mpfr.org/mpfr-current/mpfr.html#Formatted-Output-Functions)来获取BigFloat的十六进制和二进制表示:
julia> 0x7.b3b780346dc5cp+4
123.2323
julia> typeof(0x7.b3b780346dc5cp+4)
Float64
我是否在BigFloat周围的某处错过了更好的Julia API(不是ccall)到mpfr_sprintf(以及其他功能)?
编辑:对tst的一些解释。
抱歉,我认为我的答案不适合您,因为它是关于转换为字符串的。
BigFloat基于MPFR库。它有mpfr_sprintf函数,可以将输出格式化为字符串。 “%Ra”是例如mpfr_t类型的十六进制输出的格式字符串(Ref(BigFloat)是此类型的引用)。朱莉娅强调调用c-libraries。所以你可以调用mpfr_sprintf。我显示了二进制和十六进制输出。输出是空结束字符串,所以我需要从结果中解析它。 res_hex值的内容 - &gt; “0x7.b3b780346dc5cp + 4”可用于REPL或代码 - 支持十六进制浮点格式:
# [0x01, 0xec, 0xed, 0xe0, 0x0d, 0x1b, 0x71, 0x70], 6) ->
julia> 0x1.ecede00d1b7170p+6
123.2323
p + 4表示“二进制指数”(BTW.0x7b == 123(来自BigFloat(123.2323))
顺便说一句,你可以重写Dan对十六进制浮点表示的答案的结果 - &gt;
home = /path/to/virtualenv
并且BTW python具有十六进制函数以返回十六进制表示 - &gt; https://pythonhosted.org/bigfloat/reference/index.html#bigfloat.BigFloat.hex
答案 2 :(得分:0)
Dan 的(编辑过的)答案看起来比我的更可靠,但对于序列化仍然有点尴尬。
仅供参考,这是我用来从 BigFloat
到 Vector{UInt8}
的快速而肮脏的答案
function unsafe_serialize(p::BigFloat)
vcat(
reinterpret(UInt8, [p.prec]),
reinterpret(UInt8, [p.sign]),
reinterpret(UInt8, [p.exp]),
reinterpret(UInt8, unsafe_wrap(Array, p.d, (4,)))
)
end
然后返回
function unsafe_deserialize(::Type{BigFloat}, p::Array{UInt8})
Base.MPFR._BigFloat(
reinterpret(Int64, p[1:8])[1],
reinterpret(Int32, p[9:12])[1],
reinterpret(Int64, p[13:20])[1],
String(p[21:52])
)
end
这些脏的原因是我对数字和类型进行了硬编码。在第一个函数中,4
假定您的机器使用 Int64
作为 C long 类型,并且您坚持使用默认精度(或者可能稍微降低它)。在第二种情况下,这些 Int*
类型中的每一种以及相应的范围都依赖于机器,而最终范围取决于精度。这些可能可以通过实际使用正确的大小和类型来改进,但我太懒了。
我知道这些在我的机器上以默认精度工作,并且可能适用于大多数安装,但如果您有一些非标准机器,您的里程可能会有所不同(直接进入段错误)。