开发者

How to convert an 8-bit integer into a binary string in xquery?

At work today, we threw together this attempt:

xquery version "1.0";
declare option saxon:output             "omit-xml-declaration=yes";
declare variable $x := 99;

string-join(
    for $b in (128,64,32,16,8,4,2,1)
    let $xm := $x mod ($b*2)
    return
        if ( $xm >= $b ) then "1" else "0"
, "")

Do you have a better way?

Taking Oliver's answer, I have made the reverse function.

declare function local:bin-byte($x as xs:string) as xs:unsignedByte
{
  let $binary-n开发者_开发问答ibbles := ("0000", "0001", "0010", "0011", 
                          "0100", "0101", "0110", "0111",
                          "1000", "1001", "1010", "1011",
                          "1100", "1101", "1110", "1111")
  return xs:unsignedByte(
    (index-of( $binary-nibbles, substring($x,1,4) )-1) * 16
    + (index-of( $binary-nibbles, substring($x,5,4) )-1)
    )
};


As a minor note, if you are returning text, not XML then you are probably better off setting method=text rather than omit-xml-declaration=yes, although in this case it makes no difference.

An alternative solution is using a lookup table:

declare function local:binary($x as xs:unsignedByte) as xs:string
{
  let $binary-nibbles := ("0000", "0001", "0010", "0011", 
                          "0100", "0101", "0110", "0111",
                          "1000", "1001", "1010", "1011",
                          "1100", "1101", "1110", "1111")
  return concat($binary-nibbles[$x idiv 16 + 1],
                $binary-nibbles[$x mod 16 + 1])
};


The most efficient way I can think of doing the reverse (at least in XQSharp) is:

declare function local:binary-string-to-integer($binary as xs:string)
                   as xs:integer
{
  local:binary-codepoints-to-integer(string-to-codepoints($binary), 1, 0)
};

declare function local:binary-codepoints-to-integer(
                   $codepoints as xs:integer*,
                   $current-index as xs:integer,
                   $result as xs:integer)
                   as xs:integer
{
  let $current-codepoint := $codepoints[$current-index]
  return
    if (empty($current-codepoint))
    then $result
    else local:binary-codepoints-to-integer(
           $codepoints,
           $current-index + 1,
           2 * $result + $current-codepoint - string-to-codepoints("0"))
};

A quick performance test shows both methods to perform about the same when the query is interpreted, but this method is about 50% faster when the query is compiled. The recursive function method also has the benefit of not being limited to unsigned bytes.

Either way the runtime is about 10 microseconds so is nothing to be concerned about.


Recursive functions are clear if slower:

declare function local:decimal-to-binary ($d as xs:integer) as xs:string {
 if ($d > 0)
 then concat(local:decimal-to-binary(floor($d div 2)),$d mod 2)
 else ""
};

eg

local:decimal-to-binary(42)

with inverse:

declare function local:binary-to-decimal($b as xs:string) as xs:integer {
 if ($b ne "")
 then local:binary-to-decimal(substring($b, 1, string-length($b)- 1)) * 2 
       + number(substring ($b, string-length($b),1))
 else 0

};

local:binary-to-decimal(local:decimal-to-binary(42))

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜