3

I'm debugging code which contains quite a few bit shift operations, and I'm using bc a lot to look at what's happening on the bit level.

Here's what I use:

$ echo 'obase=2;598980975283696640' | bc
100001010000000000100000011000000011000000000111010000000000

Is there a simple way to get the output as whitespace-separated nibbles? E.g.

1000 0101 0000 0000 0010 0000 0110 0000 0011 0000 0000 0111 0100 0000 0000

Thanks in advance for your answers!

Edit: Thanks for the replies! However,

I tried it on another number,262148.

Should be:

100 0000 0000 0000 0100

But it is:

1000 0000 0000 0000 100

I guess the script has to search backwards through the string to get it right?

3
  • 1
    Does it have to be in bc? I would do something like... echo 'obase=2;598980975283696640' | bc | while read -n4 a; do echo -n "$a ";done;echo – Miroslav Franc Jan 1 '15 at 20:09
  • That should be an answer, already using pipes & bash, what's one more – Xen2050 Jan 1 '15 at 20:12
  • bc is not required at all btw. – vindarmagnus Jan 1 '15 at 20:15
2

I would use this simple function:

nibbles () { echo "obase=2; $1" | bc | rev | while read -n4 a; do echo -n "$a ";done | rev ; echo; }

$ nibbles 598980975283696640
1000 0101 0000 0000 0010 0000 0110 0000 0011 0000 0000 0111 0100 0000 0000 
5
  • Tried it on another number,262148. Should be: 100 0000 0000 0000 0100 But it is: 1000 0000 0000 0000 100 I guess it has to search backwards through the string? – vindarmagnus Jan 1 '15 at 20:18
  • Okay, I believe I fixed that with rev. – Miroslav Franc Jan 1 '15 at 20:22
  • That's just perfect. A thousand thanks to you :) – vindarmagnus Jan 1 '15 at 20:24
  • Shorter version with sed echo 'obase=2;598980975283696640' | bc | sed -r 's/(.{4})/\1 /g' – Fernando Jan 2 '15 at 4:53
  • Note that that syntax is bash specific. Using read -N4 a would make it compatible with ksh (not zsh where it's read -k4, or other shells that generally don't have a similar feature). – Stéphane Chazelas Aug 10 '15 at 13:44
7
echo 'obase=2;598123123980975281233696640' | BC_LINE_LENGTH=0 bc |
  rev | fold -w4 | paste -sd ' ' - | rev

Or:

echo 'obase=2;598123123980975281233696640' | BC_LINE_LENGTH=0 bc | sed ':1
  s/\(.*[01]\)\([01]\{4\}\)/\1 \2/;t1'

BC_LINE_LENGTH=0 is to stop bc from wrapping numbers at 70 columns. That's GNU specific though. rev is not a standard command, but quite common.

0

As alternative you could use the python interactive shell for it. For example:

$ python
>>> s = bin(598980975283696640)[2:]; s = '0'*(4-len(s)%4)*(len(s)%4>0)+s
>>> [s[i:i+4] for i in range(0, len(s), 4)]
['1000', '0101', '0000', '0000', '0010', '0000', '0110', '0000',
 '0011', '0000', '0000', '0111', '0100', '0000', '0000']
>>> s = bin(5989809752836966)[2:]; s = '0'*(4-len(s)%4)*(len(s)%4>0)+s
>>> [s[i:i+4] for i in range(0, len(s), 4)]
['0001', '0101', '0100', '0111', '1011', '0011', '0100', '0010',
 '1001', '0111', '0000', '1011', '0110', '0110']

Or for just one shot:

$ python -c 's = bin(598980975283696640)[2:]; s = '0'*(4-len(s)%4)*(len(s)%4>0)+s; print [s[i:i+4] for i in range(0, len(s), 4)]' 
['1000', '0101', '0000', '0000', '0010', '0000', '0110', '0000', '0011', '0000', '0000', '0111', '0100', '0000', '0000']
0

You can apply (GNU) cut to the output of bc using a custom output delimiter:

$ echo 'obase=2;5989809752836966' | BC_LINE_LENGTH=0 bc | rev \
  | cut --output-delimiter=' ' \
     -c$(echo -n 1-4; for i in $(seq 5 4 100); do echo -n ,$i-$((i+3)); done ) \ 
  | rev
1 0101 0100 0111 1011 0011 0100 0010 1001 0111 0000 1011 0110 0110

The construction of the cut LIST supplied via -c exploits the fact that unused parts of that lists are not reported as an error.

A dc version:

$ echo '5989809752836966 2 o p' | DC_LINE_LENGTH=0 dc | rev \
  | cut --output-delimiter=' ' \
     -c$(echo -n 1-4; for i in $(seq 5 4 100); do echo -n ,$i-$((i+3)); done ) \
  | rev  
1 0101 0100 0111 1011 0011 0100 0010 1001 0111 0000 1011 0110 0110
0

My preferred way would be to pad it to multiples of four:

echo 'obase=2;262148'| bc | perl -pe 'chomp; $_ = 0 x ((-length) % 4) . $_;s/([01]{4})/ $1/g;s/$/\n/'
6
  • 1
    Something like sed ':a;s/\B[01]\{4\}\b/ &/;ta' (which inserts spaces as required starting from the rightmost word boundary) should work even if the number of digits is not divisible by 4. – steeldriver Jan 1 '15 at 20:44
  • @steeldriver Which would be faster? Looping or reversing the text twice? – muru Jan 1 '15 at 20:57
  • No idea! the sed loop is one I've come across before for inserting thousands separators (just needed to change the count from 3 to 4 and the separator from comma to space) – steeldriver Jan 1 '15 at 20:59
  • Your first command can produce a group with 1–3 digits on the right. The difficulty here is to get the incomplete group on the left. – Gilles 'SO- stop being evil' Jan 1 '15 at 22:42
  • @Gilles Isn't that what the second command does? I already contemplated reversing the output, but Stephane handled that and steeldriver the other method I could think of. – muru Jan 1 '15 at 22:47
0
echo 'obase=2;262148' | bc |
   perl -E 'say readline 
                =~ s/((....)*)\n/ $1/r 
                =~ s/(\d{4})/$1 /gr'
0

Let bc and sed do the splitting into nibbles for you, and use bc and printf to do the conversion to 0-padded 4-digit binary:

i=598980975283696640
echo "obase=16;$i" | bc | sed 's/./&\n/g' | \
  xargs -I {} sh -c 'echo "ibase=16;obase=2;{}" | bc' | \
  xargs printf "%04d "
echo

1000 0101 0000 0000 0010 0000 0110 0000 0011 0000 0000 0111 0100 0000 0000

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.