Julia:BigFloat到字节数组

时间:2017-10-06 11:45:15

标签: arrays julia

我想获得BigFloat个数字的二进制数字。我认为最简单的方法是将数字转换为字节数组。在this question中有一种方法可以为Float64执行此操作,但是当我为BigFloat尝试相同时,它会告诉我,write没有BigFloat的方法。< / p>

我怎么能和朱莉娅一起做?

PS。我知道有一种方法可以将二进制数字作为字符串,但这会带来巨大的开销,我想避免它。

3 个答案:

答案 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 的(编辑过的)答案看起来比我的更可靠,但对于序列化仍然有点尴尬。

仅供参考,这是我用来从 BigFloatVector{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* 类型中的每一种以及相应的范围都依赖于机器,而最终范围取决于精度。这些可能可以通过实际使用正确的大小和类型来改进,但我太懒了。

我知道这些在我的机器上以默认精度工作,并且可能适用于大多数安装,但如果您有一些非标准机器,您的里程可能会有所不同(直接进入段错误)。