How to convert BitSet to binary string effectively? - java

I am looking for an efficient way how to easily convert a BitSet to a binary string. Let us say that its usual length would be thousands of bits.
For example, lets have this:
BitSet bits = new BitSet(8);
bits.set(1);
bits.set(3);
And this is the desired result:
String result = toBinaryString(bits);
// expected: result = "01010000"
I have some ideas in general (streams, etc.), but there might be some obvious standard method I am just missing.

So this is the most efficient way I have tried so far:
private static class FixedSizeBitSet extends BitSet {
private final int nbits;
public FixedSizeBitSet(final int nbits) {
super(nbits);
this.nbits = nbits;
}
#Override
public String toString() {
final StringBuilder buffer = new StringBuilder(nbits);
IntStream.range(0, nbits).mapToObj(i -> get(i) ? '1' : '0').forEach(buffer::append);
return buffer.toString();
}
}
Or other way using more streams:
#Override
public String toString() {
return IntStream
.range(0, nbits)
.mapToObj(i -> get(i) ? '1' : '0')
.collect(
() -> new StringBuilder(nbits),
(buffer, characterToAdd) -> buffer.append(characterToAdd),
StringBuilder::append
)
.toString();
}

String bitsStr = String.format("%8s", Integer.toBinaryString(bits.toByteArray()[0] & 0xFF)).replace(' ', '0');

Update:
It turns out that the real culprit here is BitSet.valueOf which doesn't work the way you would expect it to work. I'm going to leave my original comment here because it could be valuable to someone.
The important things to know are that if you call BitSet.valueOf(some byte array) it's not going to do what you think it should do and that you can't rely on BitSet.toByteArray() either.
import java.util.Arrays;
import java.util.BitSet;
public class MoreBitSetExperiments {
public static void main(String[] args) {
// create 10000000 00000001 00000000 as bit set using set() method
int bits = 24;
BitSet bitSet1 = new BitSet(bits);
bitSet1.set(0);
bitSet1.set(15);
// create 10000000 00000001 00000000 as bit set using valueOf() method
BitSet bitSet2 = BitSet.valueOf(new byte[] {(byte)0b10000000, (byte)0b0000001, (byte)0b00000000});
System.out.println("bitSet1.toByteArray(): " + Arrays.toString(bitSet1.toByteArray()));
System.out.println("bitSet2.toByteArray(): " + Arrays.toString(bitSet2.toByteArray()));
String binary1 = "";
String binary2 = "";
for (int i = 0; i < bits; i++) {
binary1 = binary1 + (bitSet1.get(i) ? 1 : 0);
binary2 = binary2 + (bitSet2.get(i) ? 1 : 0);
}
System.out.println("bitSet1 binary: " + binary1);
System.out.println("bitSet2 binary: " + binary2);
}
}
Output:
bitSet1.toByteArray(): [1, -128]
bitSet2.toByteArray(): [-128, 1]
bitSet1 binary: 100000000000000100000000
bitSet2 binary: 000000011000000000000000
To get back to the original question, as long as you create your bit set by calling the BitSet.set() method you can get a binary string with bits in the order you expect. But if you create your bit set using BitSet.valueOf() then your bytes will be backwards. Also, since you can't know how many 0 bytes should be at the end of your bit set, any code written to convert to binary needs to know the intended length in bytes of the output.
Original Post
I've spent the last several days debugging some issues related to bitsets so I thought I'd share.
This code demonstrates that there are 3 big unexpected behaviors with bit sets. This is because the bit set internal storage mechanism is not what you expect it to be at first glance:
import java.util.Arrays;
import java.util.BitSet;
public class BitSetExperiments {
// this class seeks to demonstrate some of the pitfalls of using the BitSet object
public static void main(String[] args) {
byte[] bytes = new byte[] {-128, 1, 0}; // 10000000 00000001 00000000 as binary
BitSet bitSet = BitSet.valueOf(bytes);
// unexpected behavior demonstrated here
System.out.println("problem 1: BitSet.toByteArray() produces unexpected results (trailing 0 bytes are not captured):");
System.out.println(" expected byte array : [-128, 1, 0]");
System.out.println("actual bitset.toByteArray(): " + Arrays.toString(bitSet.toByteArray()));
System.out.println();
System.out.println("problem 2: BitSet stores the bits of a byte in the wrong order:");
System.out.println("expected binary: 100000000000000100000000");
System.out.print("actual binary: ");
for (int i = 0; i < bytes.length * 8; i++) {
System.out.print(bitSet.get(i) ? 1 : 0);
}
System.out.println("\n");
System.out.println("problem 3: bitSet cannot accurately tell you the length of bytes or bits stored");
System.out.println("expected bit/byte length is: 24 bits or 3 bytes");
System.out.println("bitSet.size(): " + bitSet.size());
System.out.println("bitSet.length(): " + bitSet.length());
System.out.println("bitSet.cardinality(): " + bitSet.cardinality());
System.out.println();
}
}
Running this code shows:
problem 1: BitSet.toByteArray() produces unexpected results (trailing 0 bytes are not captured):
expected byte array : [-128, 1, 0]
actual bitset.toByteArray(): [-128, 1]
problem 2: BitSet stores the bits of a byte in the wrong order:
expected binary: 100000000000000100000000
actual binary: 000000011000000000000000
problem 3: bitSet cannot accurately tell you the length of bytes or bits stored
expected bit/byte length is: 24 bits or 3 bytes
bitSet.size(): 64
bitSet.length(): 9
bitSet.cardinality(): 2
Ok, so knowing these 3 unexpected behaviors, we can code around them. But the important thing to realize is that you don't know how many trailing 0 bytes are on your bit set, and bit set won't tell you. This means when you want to convert the bit set to binary, you need to be explicit about how many bytes you're expecting.
Here's what I came up with:
public static String convertBitSetToBinaryString(BitSet bitSet, int lengthInBytes) {
if(bitSet == null) {
return null;
}
if(lengthInBytes < 0) {
return "";
}
StringBuilder binary = new StringBuilder();
// compute each byte, and then reverse them before appending to the running binary
for(int i = 0; i < lengthInBytes; i++) {
StringBuilder backwardsByte = new StringBuilder();
for(int j = 0; j < Byte.SIZE; j++) {
backwardsByte.append(bitSet.get((i*Byte.SIZE)+j) ? "1" : "0");
}
binary.append(StringUtils.reverse(backwardsByte.toString()));
}
return binary.toString();
}
note: uses StringUtils.reverse() from apache commons
And here is the test to make sure it actually works:
void testConvertBitSetToBinaryString() {
assertNull(convertBitSetToBinaryString(null, 0));
assertNull(convertBitSetToBinaryString(null, 1));
assertEquals("00000000", convertBitSetToBinaryString(BitSet.valueOf(new byte[] {}), 1));
assertEquals("00000000", convertBitSetToBinaryString(BitSet.valueOf(new byte[] {0}), 1));
assertEquals("10000000", convertBitSetToBinaryString(BitSet.valueOf(new byte[] {-128}), 1));
assertEquals("00000001", convertBitSetToBinaryString(BitSet.valueOf(new byte[] {1}), 1));
assertEquals("0000000100000000", convertBitSetToBinaryString(BitSet.valueOf(new byte[] {1}), 2));
assertEquals("0000000100000010", convertBitSetToBinaryString(BitSet.valueOf(new byte[] {1, 2}), 2));
assertEquals("1000000010000000", convertBitSetToBinaryString(BitSet.valueOf(new byte[] {-128, -128}), 2));
assertEquals(
"00000001 01001101 10000000 00000000".replace(" ", ""),
convertBitSetToBinaryStringV2(BitSet.valueOf(new byte[] {1, 77, -128, 0}), 4)
);
}

Related

Replace all inside bitset with a differently sized bitset

I'm currently dealing with a binary file that will later on be written into a different binary file. This is very important and is the reason I'm hesitant to use ArrayLists and other lists, as they tend to not play nicely with me trying to write it into a file directly.
I've retrieved the bytes out of this binary and separated them into bits using BitSet. I think I've figured out how to find the bitset I want to replace. Currently this looks kinda like this:
try {
InputStream inputStream = new FileInputStream(filepath);
OutputStream outputStream = new FileOutputStream("output.bin");
byte[] buffer = new byte[4096];
BitSet bitSet = new BitSet(4096 * 8);
BitSet bitString = new BitSet(search.length());
BitSet bitReplace = new BitSet(replace.length());
// Search String to bitset
for (int i = search.length() - 1; i >= 0; i--) {
if (search.charAt(i) == '1') {
bitString.set(search.length() - i - 1);
}
}
// Replace String to bitset
for (int i = replace.length() - 1; i >= 0; i--) {
if (replace.charAt(i) == '1') {
bitReplace.set(replace.length() - i - 1);
}
}
while (inputStream.read(buffer) != -1) {
bitSet = BitSet.valueOf(buffer);
bufferCount++;
// GET 4096 BYTES AT THE SAME TIME
// TURN THEM INTO BITS, WE END UP WITH 4096*8 bits
// COMPARE EVERY SEARCHSIZE BITS
for (int i = 0; i < bitSet.length(); i++) {
if (bitSet.get(i, i + bitString.length()).equals(bitString)) {
//TODO: Replace bitset with a different bit set
}
}
}
inputStream.close();
outputStream.close();
} catch (IOException e) {
System.out.println("IOException");
System.exit(1);
}
What I'm missing is how to set an existing bitsets once the pattern of bits have been found with a different bitset(could be differently sized).
So to illustrate:
Find: 01010 replace with: 001111
Would turn this sequence of bits:
00|01010|01000000000000010
into:
00|001111|010000000000000010
Abstractly I've thought of a solution, to be like this:
1. Find the pattern that matches the SEARCHed pattern
2. Replace a bitset with a completely different bitset(this is what I'm struggling with, I was thinking about just appending everything to the end of the file, but that would not be very efficient in terms of read/write
3. Shift the other bits to the left or to the right based on the difference between the sizes of the searched pattern and the pattern we're replacing with.
4. Write into file.
You could define a function setBitsFromIndex(int i, BitSet source, BitSet dest):
private static void setBitsFromIndex(int i, BitSet source, BitSet dest) {
for (int j = 0; j < source.length(); j++) {
dest.set(i+j, source.get(j));
}
}
Then, in your code:
for (int i = 0; i < bitSet.length() - bitString.length(); i++) {
if (bitSet.get(i, i + bitString.length()).equals(bitString)) {
//Replace bitset with a different bit set
BitSet tempBitSet = bitSet.get(i + bitString.length(), bitSet.length());
setBitsFromIndex(i, bitReplace, bitSet);
setBitsFromIndex(i + bitReplace.length(), tempBitSet, bitSet);
// if bitReplace is shorter than bitString, we may need to clear trailing bits
if (bitReplace.length() < bitString.length()) {
bitSet.clear(i + bitReplace.length() + tempBitSet.length(), bitSet.length());
}
break;
}
}
BE WARNED: The length of a BitSet is NOT it's capacity, or even the length it was prior to the last time you set a bit. It is the index + 1 of the HIGHEST SET (1) BIT, so your bitReplace, bitString, and bitSet BitSets might not be the length you think they are if they have 0s in the most-significant bits. If you want to include leading zeros, you have to track the desired size of your bitReplace and bitString BitSets independently.

How to convert an integer value into 'n' digit ascii byte array in java

Is any method to convert an integer value into 'n' digit ascii byte array in java. Like
IntToBytes( int value, int digits );
int a = 1234;
IntToBytes( a, 4 );
results will be byte array of {1, 2, 3, 4}
IntToBytes( a, 5 );
results will be byte array of {0, 1, 2, 3, 4}
I searched in internet no method found. Please help me
If you want to obtain an array with the values of the digits in decimal notation, you can create a function like this one:
public static byte[] intToBytes(int number, int digits) {
byte[] results = new byte[digits];
/* Start at the end of the array. i.e. 1234, should be converted to {1, 2, 3, 4} */
for (int i = digits - 1; i >= 0; i--) {
results[i] = (byte) (number % 10);
number = number / 10;
}
return results;
}
If what you want is an array with the ascii value of each digit, you can use String.format and convert the String to the array of bytes.
(1234 should be converted to {'1', '2', '3', '4'}. The ascii values would be {49, 50, 51, 52}
The code could be:
public static byte[] intToBytes(int number, int digits) {
String format = "%0" + digits + "d";
String numberAsString = String.format(format, number);
char[] numberAsCharArray = numberAsString.toCharArray();
byte[] result = new byte[digits];
for (int i = 0; i < numberAsCharArray.length; i++) {
result[i] = (byte) numberAsCharArray[i];
}
return result;
}
Within apache commons lang there is the class org.apache.commons.lang3.Conversion which has the method longToByteArray. That might be helpful for what you are attempting to do.
I think your precise question is how to convert an int number representation into a series of its digits in decimal notation.
I do not know of any existing (i.e. library) method for this, but you could easily write your own using one of the obvious ways. One of them would be to repetitively take remainder from division (a.k.a. Horner's method). The other one could be to convert the int to String, and then convert this Strings characters to bytes by casting.
you can use String.format() function
the example may be helpful for you
String s = String.format("%06d", 1234);
System.out.println(s.toCharArray());
hope that helped
Straight from my project Euler library. :)
The algorithm does amod 10 to get the last digit, then divides by 10 to shift the number right and remove the last digits. This repeats until zero. In my algorithm I return an array, so I use log10 to get the exact number of digits. You can optionally just stick this in a while loop and divide by 10 until equals 0 and append mod 10 to a List.
public static int[] getDigits(long n) {
final int digits = numDigits(n);
final int[] d = new int[digits];
int i = 0;
while (n > 0) {
d[i] = (int)(n % 10);
n /= 10;
i++;
}
return d;
}
public static int numDigits(final long n) {
return (int) Math.log10(n) + 1;
}
It should be pretty trivial to adapt this to return an array of bytes.
I think you shoud use the operation modulo.
You have to create a digits size char array and then, iterating from the end, insert value%10 - '0' (last digit converted into a char) in that position and dividing value by 10 in each iteration. When value = 0 you just have to insert '0' into the remaining positions.

Ensuring that an XOR operation on strictly positive longs only yield strictly positive longs

I want to ensure that an XOR operation on strictly positive longs only yields strictly positive longs.
My question is base on the following java code:
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;
public class Test {
static Random r = new Random();
static class Data {
long id = Math.abs(r.nextLong());
#Override
public String toString() {
return "Data {" + "id=" + id + '}';
}
}
public static void main(String[] args) {
List<Data> data = new ArrayList<>();
for (int i = 0; i < 10; i++) {
data.add(new Data());
}
final String password = "don't you ever tell them";
byte[] passwordBytes = password.getBytes();
long[] passwordLongs = new long[passwordBytes.length / 8];
for (int i = 0; i < passwordLongs.length; i++) {
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
byte[] chunk = new byte[Long.BYTES];
System.arraycopy(passwordBytes, i * Long.BYTES, chunk, 0, Long.BYTES);
buffer.put(chunk);
buffer.flip();//need flip
passwordLongs[i] = buffer.getLong();
}
System.out.println(data);
ListIterator<Data> encryptIterator = data.listIterator();
while (encryptIterator.hasNext()) {
Data next = encryptIterator.next();
next.id = next.id ^ passwordLongs[(encryptIterator.nextIndex() - 1) % passwordLongs.length];//XOR here
}
System.out.println(data);
}
}
Can anyone please provide an answer possibly with some theory?
Invariant 1: a positive integer's most significant bit is zero.
Invariant 2: 0 XOR 0 = 0.
Conclusion: positive integer XOR positive integer = positive integer.
Given that longs in Java are always signed, you should make sure that every long from your "password" never toggles the sign bit.
That is, all your longs in your password should be like 0b0xxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx.
This weakens the "encryption" (exactly) a bit, but you should not worry much over this.
It should however be noted that unless you want to compare them being greater than 0, there is no reason doing this; the actual number will always be the same, that is, 0xff or 0b11111111 is always the same, only its decimal representation changes according to whether you are using unsigned or signed short integer to store it (255 and -1 respectively).

Convert a given decimal string into a binary string(even number of binary digits) in Java [duplicate]

for example, for 1, 2, 128, 256 the output can be (16 digits):
0000000000000001
0000000000000010
0000000010000000
0000000100000000
I tried
String.format("%16s", Integer.toBinaryString(1));
it puts spaces for left-padding:
` 1'
How to put 0s for padding. I couldn't find it in Formatter. Is there another way to do it?
P.S. this post describes how to format integers with left 0-padding, but it is not for the binary representation.
I think this is a suboptimal solution, but you could do
String.format("%16s", Integer.toBinaryString(1)).replace(' ', '0')
There is no binary conversion built into the java.util.Formatter, I would advise you to either use String.replace to replace space character with zeros, as in:
String.format("%16s", Integer.toBinaryString(1)).replace(" ", "0")
Or implement your own logic to convert integers to binary representation with added left padding somewhere along the lines given in this so.
Or if you really need to pass numbers to format, you can convert your binary representation to BigInteger and then format that with leading zeros, but this is very costly at runtime, as in:
String.format("%016d", new BigInteger(Integer.toBinaryString(1)))
Here a new answer for an old post.
To pad a binary value with leading zeros to a specific length, try this:
Integer.toBinaryString( (1 << len) | val ).substring( 1 )
If len = 4 and val = 1,
Integer.toBinaryString( (1 << len) | val )
returns the string "10001", then
"10001".substring( 1 )
discards the very first character. So we obtain what we want:
"0001"
If val is likely to be negative, rather try:
Integer.toBinaryString( (1 << len) | (val & ((1 << len) - 1)) ).substring( 1 )
You can use Apache Commons StringUtils. It offers methods for padding strings:
StringUtils.leftPad(Integer.toBinaryString(1), 16, '0');
I was trying all sorts of method calls that I haven't really used before to make this work, they worked with moderate success, until I thought of something that is so simple it just might work, and it did!
I'm sure it's been thought of before, not sure if it's any good for long string of binary codes but it works fine for 16Bit strings. Hope it helps!! (Note second piece of code is improved)
String binString = Integer.toBinaryString(256);
while (binString.length() < 16) { //pad with 16 0's
binString = "0" + binString;
}
Thanks to Will on helping improve this answer to make it work with out a loop.
This maybe a little clumsy but it works, please improve and comment back if you can....
binString = Integer.toBinaryString(256);
int length = 16 - binString.length();
char[] padArray = new char[length];
Arrays.fill(padArray, '0');
String padString = new String(padArray);
binString = padString + binString;
A simpler version of user3608934's idea "This is an old trick, create a string with 16 0's then append the trimmed binary string you got ":
private String toBinaryString32(int i) {
String binaryWithOutLeading0 = Integer.toBinaryString(i);
return "00000000000000000000000000000000"
.substring(binaryWithOutLeading0.length())
+ binaryWithOutLeading0;
}
I do not know "right" solution but I can suggest you a fast patch.
String.format("%16s", Integer.toBinaryString(1)).replace(" ", "0");
I have just tried it and saw that it works fine.
Starting with Java 11, you can use the repeat(...) method:
"0".repeat(Integer.numberOfLeadingZeros(i) - 16) + Integer.toBinaryString(i)
Or, if you need 32-bit representation of any integer:
"0".repeat(Integer.numberOfLeadingZeros(i != 0 ? i : 1)) + Integer.toBinaryString(i)
try...
String.format("%016d\n", Integer.parseInt(Integer.toBinaryString(256)));
I dont think this is the "correct" way to doing this... but it works :)
I would write my own util class with the method like below
public class NumberFormatUtils {
public static String longToBinString(long val) {
char[] buffer = new char[64];
Arrays.fill(buffer, '0');
for (int i = 0; i < 64; ++i) {
long mask = 1L << i;
if ((val & mask) == mask) {
buffer[63 - i] = '1';
}
}
return new String(buffer);
}
public static void main(String... args) {
long value = 0b0000000000000000000000000000000000000000000000000000000000000101L;
System.out.println(value);
System.out.println(Long.toBinaryString(value));
System.out.println(NumberFormatUtils.longToBinString(value));
}
}
Output:
5
101
0000000000000000000000000000000000000000000000000000000000000101
The same approach could be applied to any integral types. Pay attention to the type of mask
long mask = 1L << i;
A naive solution that work would be
String temp = Integer.toBinaryString(5);
while (temp.length() < Integer.SIZE) temp = "0"+temp; //pad leading zeros
temp = temp.substring(Integer.SIZE - Short.SIZE); //remove excess
One other method would be
String temp = Integer.toBinaryString((m | 0x80000000));
temp = temp.substring(Integer.SIZE - Short.SIZE);
This will produce a 16 bit string of the integer 5
// Below will handle proper sizes
public static String binaryString(int i) {
return String.format("%" + Integer.SIZE + "s", Integer.toBinaryString(i)).replace(' ', '0');
}
public static String binaryString(long i) {
return String.format("%" + Long.SIZE + "s", Long.toBinaryString(i)).replace(' ', '0');
}
This is an old trick, create a string with 16 0's then append the trimmed binary string you got from String.format("%s", Integer.toBinaryString(1)) and use the right-most 16 characters, lopping off any leading 0's. Better yet, make a function that lets you specify how long of a binary string you want. Of course there are probably a bazillion other ways to accomplish this including libraries, but I'm adding this post to help out a friend :)
public class BinaryPrinter {
public static void main(String[] args) {
System.out.format("%d in binary is %s\n", 1, binaryString(1, 4));
System.out.format("%d in binary is %s\n", 128, binaryString(128, 8));
System.out.format("%d in binary is %s\n", 256, binaryString(256, 16));
}
public static String binaryString( final int number, final int binaryDigits ) {
final String pattern = String.format( "%%0%dd", binaryDigits );
final String padding = String.format( pattern, 0 );
final String response = String.format( "%s%s", padding, Integer.toBinaryString(number) );
System.out.format( "\npattern = '%s'\npadding = '%s'\nresponse = '%s'\n\n", pattern, padding, response );
return response.substring( response.length() - binaryDigits );
}
}
This method converts an int to a String, length=bits. Either padded with 0s or with the most significant bits truncated.
static String toBitString( int x, int bits ){
String bitString = Integer.toBinaryString(x);
int size = bitString.length();
StringBuilder sb = new StringBuilder( bits );
if( bits > size ){
for( int i=0; i<bits-size; i++ )
sb.append('0');
sb.append( bitString );
}else
sb = sb.append( bitString.substring(size-bits, size) );
return sb.toString();
}
You can use lib https://github.com/kssource/BitSequence. It accept a number and return bynary string, padded and/or grouped.
String s = new BitSequence(2, 16).toBynaryString(ALIGN.RIGHT, GROUP.CONTINOUSLY));
return
0000000000000010
another examples:
[10, -20, 30]->00001010 11101100 00011110
i=-10->00000000000000000000000000001010
bi=10->1010
sh=10->00 0000 0000 1010
l=10->00000001 010
by=-10->1010
i=-10->bc->11111111 11111111 11111111 11110110
for(int i=0;i<n;i++)
{
for(int j=str[i].length();j<4;j++)
str[i]="0".concat(str[i]);
}
str[i].length() is length of number say 2 in binary is 01 which is length 2
change 4 to desired max length of number. This can be optimized to O(n).
by using continue.
import java.util.Scanner;
public class Q3{
public static void main(String[] args) {
Scanner scn=new Scanner(System.in);
System.out.println("Enter a number:");
int num=scn.nextInt();
int numB=Integer.parseInt(Integer.toBinaryString(num));
String strB=String.format("%08d",numB);//makes a 8 character code
if(num>=1 && num<=255){
System.out.println(strB);
}else{
System.out.println("Number should be in range between 1 and 255");
}
}
}

How to convert java BigDecimal to normal byte array (not 2's complement)

How do I convert from big integer to a byte array which is not in 2's complement format. Bascially I only need to convert positive numbers and do not need the sign bit.
So something like 10 would become a byte 0x0a i.e-> 00001010
[Update]
As per comment I tried this
public void testBinary()
{
BigDecimal test = new BigDecimal(35116031);
BigInteger theInt = test.unscaledValue();
byte[] arr = theInt.toByteArray();
System.out.println(getCounterVal(arr, new BigInteger("256")));
}
public BigInteger getCounterVal(byte[] arr, BigInteger multiplier)
{
BigInteger counter = BigInteger.ZERO;
for(int i = (arr.length - 1); i >=0; i--)
{
int b = arr[i];
//int val = (int) b & 0xFF;
BigInteger augend = BigInteger.valueOf(b);
counter = counter.add(augend.multiply(multiplier.pow(i)));
}
return counter;
}
The out put value I got was -19720446 And with the //int val = (int) b & 0xFF; uncommented and used as augend, I got the value 4292024066
[Update2]
Here is a test I ran which works. Not sure if it is bug free but looks fine.
#Test
public void bigIntegerToArray()
{
BigInteger bigInt = new BigInteger("35116444");
byte[] array = bigInt.toByteArray();
if (array[0] == 0)
{
byte[] tmp = new byte[array.length - 1];
System.arraycopy(array, 1, tmp, 0, tmp.length);
array = tmp;
}
BigInteger derived = BigInteger.ZERO;
BigInteger twofiftysix = new BigInteger("256");
int j = 0;
for (int i = array.length - 1; i >= 0; i--)
{
int val = (int) array[i] & 0xFF;
BigInteger addend = BigInteger.valueOf(val);
BigInteger multiplier = twofiftysix.pow(j);
addend = addend.multiply(multiplier);
derived = derived.add(addend);
j++;
}
Assert.assertEquals(bigInt, derived);
}
The difference is largely conceptual. Unsigned numbers are the same in 2's compliment. 2's compliment just describes how to represent negative numbers which you say you don't have.
i.e. 10 is 00001010 in signed and unsigned representation.
To get the bytes from a BigDecimal or BigInteger you can use the methods it provides.
BigDecimal test = new BigDecimal(35116031);
BigInteger theInt = test.unscaledValue();
byte[] arr = theInt.toByteArray();
System.out.println(Arrays.toString(arr));
BigInteger bi2 = new BigInteger(arr);
BigDecimal bd2 = new BigDecimal(bi2, 0);
System.out.println(bd2);
prints
[2, 23, -45, -1]
35116031
The bytes are correct and reproduce the same value.
There is a bug in the way you rebuild your BigInteger. You assume the byte serialization is little endian when Java typically uses big endian http://en.wikipedia.org/wiki/Endianness
Try to split the number in bytes, by dividing by 256 in each iteration and using the remainder, and place all these bytes into an array.
the sign bit in 2-compliment for positive numbers is 0
so signed or unsigned doesn't make a difference for positive numbers
If the value is less than the size of a long then use longValue, then chop the long into bytes. If the value is bigger than a long then probably you need to use an iterative approach, repeatedly dividing the number by 256, taking the remainder as the next byte, then repeating until you get zero. The bytes would be generated right to left. Signed numbers require thought (to generate 2s-complement results) but aren't much more complicated.

Categories

Resources