It seems I have a two's complement issue with Java's BigInteger.
I have a 64-bit integer where only the msb and the second msb are set to 1, the rest is 0.
In decimal this comes up to: -4611686018427387904
The Java side of my application receives this decimal number as a string, and converts it to BigInteger like so:
BigInteger bi = new BigInteger("-4611686018427387904", 10);
Then, it needs to display this number both in binary and hex forms.
I tried to use:
String bin = bi.toString(2);
String hex = bi.toString(16);
but I'm getting:
-100000000000000000000000000000000000000000000000000000000000000
-4000000000000000
whereas I expect to get:
1100000000000000000000000000000000000000000000000000000000000000
c000000000000000
Any tips?
Number always fits in 64 bits:
If your number always fits in 64 bits you can put it in a long and then print the bits / hex digits.
long l = bi.longValue();
String bin = Long.toBinaryString(l);
String hex = Long.toHexString(l);
System.out.println(bin);
System.out.println(hex);
Number may not always fit in 64 bits:
If the number does not always fit in 64 bits, you'll have to solve it "manually". To convert a number to it's two's complement representation you do the following:
If number is positive, do nothing
If number is negative:
Convert it to its absolute value
Complement the bits
Add 1
For a BigInteger the conversion looks as follows:
if (bi.compareTo(BigInteger.ZERO) < 0)
bi = bi.abs().not().add(BigInteger.ONE);
If you print it using bi.toString(2) you'll still get the sign character, instead of a leading 1. This can be solved by simply appending .replace('-', '1') to the string.
There is a BigInteger.toByteArray() method, that returns two's complement representation of BigInteger as a byte[]. All you need is to print that array in hex or binary form:
byte[] bs = bi.toByteArray();
for (byte b: bs) {
System.out.print(String.format("%02X", 0xff & b));
}
The binary number 1100000000000000000000000000000000000000000000000000000000000000 is definitely a positive number, right. It's equal to 2^63 + 2^62.
I don't see why you'd expect a negative number to become positive when you convert to base 2 or base 16.
You are confusing the base n representation with the internal representation of numbers.
If the number is 64 bits or less, then the simple way to solve this is to convert to a long and then use Long.toHexString().
what you mean?
Do you want to get Two's complement?
if you mean that, maybe i can give you an example
import java.util.*;
public class TestBina{
static void printBinaryInt(int i){
System.out.println("int:"+i+",binary:");
System.out.print(" ");
for(int j=31;j>=0;j--)
if(((1<<j)&i)!=0)
System.out.print("1");
else
System.out.print("0");
System.out.println();
}
public static void main(String [] args){
Random rand = new Random();
int i = rand.nextInt();
int j = rand.nextInt();
printBinaryInt(i);
printBinaryInt(j);
printBinaryInt(10);
printBinaryInt(-10);
}
}
Related
I am attempting to convert some BigInteger objects and math from Java to C#.
The Java flow is as follows:
1. Construct 2 BigIntegers from a base-10 string (i.e. 0-9 values).
2. Construct a third BigInteger from an inputted byte array.
3. Create a fourth BigInteger as third.modPow(first, second).
4. Return the byte result of fourth.
The main complications in converting to C# seem to consist of endianness and signed/unsigned values.
I have tried a couple different ways to convert the initial 2 BigIntegers from Java->C#. I believe that using the base-10 string with BigInteger.Parse will work as intended, but I am not completely sure.
Another complication comes from the use of a BinaryReader/BinaryWriter implementation, in C#, that is already big-endian (like Java). I use the BR/BW to supply the byte array to create the third BigInteger and consume the byte array produced from the modPow (the fourth BigInteger).
I have tried reversing the byte arrays for input and output in every way, and still do not get the expected output.
Java:
public static byte[] doMath(byte[] input)
{
BigInteger exponent = new BigInteger("BASE-10-STRING");
BigInteger mod = new BigInteger("BASE-10-STRING");
BigInteger bigInput = new BigInteger(input);
return bigInput.modPow(exponent, mod).toByteArray();
}
C#:
public static byte[] CSharpDoMath(byte[] input)
{
BigInteger exponent = BigInteger.Parse("BASE-10-STRING");
BigInteger mod = BigInteger.Parse("BASE-10-STRING");
// big->little endian
byte[] reversedBytes = input.Reverse().ToArray();
BigInteger bigInput = new BigInteger(reversedBytes);
BigInteger output = BigInteger.ModPow(bigInput, exponent, mod);
// little->big endian
byte[] bigOutput = output.ToByteArray().Reverse().ToArray();
return bigOutput;
}
I need the same output from both.
I am generating modulus and exponent from Java and .NET system but there are differences in both. I need the out come like .NET in java. Java is adding two extra zeros on hex conversion of modulus and in exponent removing 1 zero but .NET is generating correct. Please see below results from .Net and Java.
If I use toString(16) then its generate below results. toString(16) is not adding two zeros in modulus but removing a zero from exponent where .NET add a 0 in exponent and remove two zeros from modulus which is I want.
String modlusHexString = publicKey.getModulus().toString(16).toUpperCase();
String exponentHexString = publicKey.getPublicExponent().toString(16).toUpperCase();
ModlusHex toString(16): D9B4E023A7CEF604499E184CBA7B7847FE35A824D15FF902EADB952FB54620158A564EFDDB0A66A7647CBDB339359BF6756F5851A73CC1D24859A064DD7AE30B2330F965C54682B10E886D35FE005F42B056C7ABF64D3F6D592AEDC0234417507A0A1432E51C7867E3ACC4867A1AE03EF9E62050180882B18771D5703C8BADCB3AC767CD1A1C0F9344F10B8C82EF5D0ACA4422512EA3ECCB5B71097BDEDAD9BBBE11697D1E61814CF3BBDEB48BDC2C95AA10DFC3F7F794E307D49B5455F928A9BB3ED2F28D6E2974238EFB2D9A822EC1832177CB988206204DF1D9DB7D291E2816576BEBD669184894B526F0B5D10C7D19FA67E79DADDF97D4A3082D4812A27B
ExponentHex toString(16): 10001
I tried below method also to convert BigInteger of modulus and exponent to Hex but no luck-
static String toHex(byte[] ba) {
StringBuilder hex = new StringBuilder(ba.length * 2);
for (byte b : ba) {
hex.append(String.format("%02x", b, 0xff));
}
return hex.toString().toUpperCase();
}
Modlus Hex: 00D9B4E023A7CEF604499E184CBA7B7847FE35A824D15FF902EADB952FB54620158A564EFDDB0A66A7647CBDB339359BF6756F5851A73CC1D24859A064DD7AE30B2330F965C54682B10E886D35FE005F42B056C7ABF64D3F6D592AEDC0234417507A0A1432E51C7867E3ACC4867A1AE03EF9E62050180882B18771D5703C8BADCB3AC767CD1A1C0F9344F10B8C82EF5D0ACA4422512EA3ECCB5B71097BDEDAD9BBBE11697D1E61814CF3BBDEB48BDC2C95AA10DFC3F7F794E307D49B5455F928A9BB3ED2F28D6E2974238EFB2D9A822EC1832177CB988206204DF1D9DB7D291E2816576BEBD669184894B526F0B5D10C7D19FA67E79DADDF97D4A3082D4812A27B
Exponent : 010001
Following is .NET generated HEX of public key modulus and exponent which is correct
.NET
Modulus HEX:
F86020AFD75A03911BE8818BCB506B5DAC2760C68BB46F2A53E10E3E0972A7FFFFF71EAC0E0D73E8FDB4C332C759E781E54C0F1F4637656E6E995873B9580027F49606811C7B5DB458C1BAC3E3D6EB7B77BFE1E55A822F23797E4CB27C7BD88C1B782AEF5235DAE55B937ABB0FFD30AF64F8D69DA07946441D9E4704FA98BD026E00A92851FABC8AB347AB75615ACC8A7CCFDE56B0797DDB70FCA1F28F23F86548AABE6DD89B5CC859BC6352D077F765AAFCF15695215850A8D7E1F9DF187AE0EF1934E096B739E884757F810B3320EFA72BBE2A957CE465E2010A5FD9C96A5F6456658D3BA0DF51B472AFEBEF31C3609B58C6A03C671DA33650039822465179
Exponent : 010001
The problem you are facing results from the behavior of the methods BigInteger.toString(int radix) and BigInteger.toByteArray().
When you call the BigInteger.toString(int radix) method, it returns only the significant digits of the number. So if the value is supposed to be, for example, 05ABFF, it returns only 5ABFF. This is natural when the radix is 10 (we don't expect the big integer 13 to have be converted to something like 013), but this is somewhat counter-intuitive when the radix is 16, as you expect the output to have an even length, exactly two characters for each byte. But that's not how it works.
But when you call your own toHex() method, it is based on the value returned from BigInteger.toByteArray(). Here you have your other problem. This method always returns the number of bytes necessary to represent the number, including a sign bit. Now consider the number 0xD9B4E023. This is actually a negative number if it is considered an integer, but if it is considered as positive by BigInt, you need an extra byte that represents the sign. Hence the additional byte that translates to 00 in your method.
I can think of two possible solutions:
static String toHex(byte[] ba) {
StringBuilder hex = new StringBuilder(ba.length * 2);
boolean skipZeroBytes = true;
for (byte b : ba) {
// As soon as we hit the first non-zero byte, we stop skipping bytes
if (b != 0) {
skipZeroBytes = false;
}
// If the current byte is zero, and we are in skipping mode, skip
if (skipZeroBytes) {
continue;
}
hex.append(String.format("%02X", b, 0xff));
}
if (skipZeroBytes) {
// If we are still in skipping mode, it means all the bytes in the
// array were zero and we skipped them all. So just return the
// representation of a zero.
return "00";
} else {
return hex.toString();
}
}
What we do here is skip all the initial zero bytes until we hit the first non-zero byte, and only then we start interpreting it. Small note: using the format %02X with a capital X gives you uppercase hexadecimal digits and saves the need to call toUpperCase() later.
The other, simpler method is to add the missing zero to the result of BigInteger.toString(int radix):
static String toHex2(BigInteger bi) {
String hex = bi.toString(16).toUpperCase();
if (hex.length() % 2 == 1) {
return "0" + hex;
} else {
return hex;
}
}
I'm having a small error in my code that I can not for the life of me figure out.
I have an array of strings that are representations of binary data (after converting them from hex) for example:
one index is 1011 and another is 11100. I go through the array and pad each index with 0's so that each index is eight bytes. When I try to convert these representations into actual bytes I get an error when I try to parse '11111111' The error I get is:
java.lang.NumberFormatException: Value out of range. Value:"11111111" Radix:2
Here is a snippet:
String source = a.get("image block");
int val;
byte imageData[] = new byte[source.length()/2];
try {
f.createNewFile();
FileOutputStream output = new FileOutputStream(f);
for (int i=0; i<source.length(); i+=2) {
val = Integer.parseInt(source.substring(i, i+2), 16);
String temp = Integer.toBinaryString(val);
while (temp.length() != 8) {
temp = "0" + temp;
}
imageData[i/2] = Byte.parseByte(temp, 2);
}
Isn't the problem here that byte is a signed type, therefore its valid values are -128...127? If you parse it as an int (Using Integer.parseInt()), it should work.
By the way, you don't have to pad the number with zeroes either.
Once you parsed your binary string into an int, you can cast it to a byte, but the value will still be treated as signed, so binary 11111111 will become int 255 first, then byte -1 after the cast.
Well, eight one's is 255, and according to java.lang.Byte, the MAX_VALUE is 2^7 - 1 or positive 127.
So your code will fail because you number is too large. The first bit is reserved for the positive and negative sign.
according to parseByte
byte's only allow numbers in the range of -128 to 127. I would use an int instead, which holds numbers in the range of -2.1 billion to 2.1 billion.
How does one obtain the complementary hexadecimal value for a given input ?
This could be a bit more generic, i.e. having an array of X possible values, how do you convert a random array like arr[x] --> arr[arr.length - arr.indexOf(x)].
Please ignore the syntax.
The following code snippet will find 16 complement of hexadecimal number:
BigInteger subtrahend = new BigInteger("2D", 16);
// input, you can take input from user and use after validation
char[] array = new char[subtrahend.toString(16).length()];
// construct a character array of the given length
Arrays.fill(array, 'F');
// fill the array by F, look at the first source there the FF is subtracted by 2D
BigInteger minuend = new BigInteger(new String(array), 16);
// construct FFF... Biginteger of that length
BigInteger difference = minuend.subtract(subtrahend);
// calculate minus
BigInteger result = difference.add(BigInteger.ONE);
// add one to it
System.out.println(result.toString(16));
// print it in hex format
Hope this will help you. Thank you.
Source:
Binary and Hexadecimal Arithmetic
Digital Principles and Logic Design
First find the 15's complement of the given input by subtracting it from the number FF... which is of the same length of the input. Then add 1 to it.
Hi i want to convert a long integer to binary but the problem is i want a fixed 16 bit binary result after conversion like if i convert 2 to 16 bit binary it should give me 0000000000000010 as ans can anyone help me ?
Most likely what you want is Integer.toBinaryString(), combined with something to ensure that you get exactly 16 places:
int val = 2;
String bin = Integer.toBinaryString(0x10000 | val).substring(1);
The idea here is to get the zero padding by putting a 1 in the 17th place of your value, and then use String.substring() to chop off the leading 1 this creates, thus always giving you exactly 16 binary digits. (This works, of course, only when you are certain that the input is a 16-bit number.)
I'm presuming that you want a String output of fixed length (16). Here's what the code would look like:
String binarized = Integer.toBinaryString(i);
int len = binarized.length();
String sixteenZeroes = "00000000000000000";
if (len < 16)
binarized = sixteenZeroes.subString(0, 16-len).concat(binarized);
else
binarized = binarized.subString(len - 16);
return binarized;
Warning: I didn't compile or run it, so make sure no bug is there :)
In contrast to many suggestions here: Integer.toBinaryString, doesn't work for a 16 bit (a short) and it will not print leading zero's. The reason is that (as the name suggests) this will only work for integers. And for negative numbers the bit representation will change (the first bit indicates a negative number). The two numbers below represent the same number in short and int. So if you want to represent the raw bits you have received (this is the general application of your problem), this function will generate strange output.
decimal: -3
short: 1111 1111 1111 1101
int: 1111 1111 1111 1111 1111 1111 1111 1101
EDIT: Changed the number above
Hence you can not cast the short if you are interested in the bit.
Java doesn't provide the implementation for short, so you will have to provide your own. Something like this (size is the number of bits):
int displayMask = 1 << (size - 1);
StringBuffer buf = new StringBuffer( size);
for ( int c = 1; c <= size; c++ )
{
buf.append( ( value & displayMask ) == 0 ? '0' : '1' );
value <<= 1;
}
I had to do it for a 32 bit number and ended up with:
String stringWord = Long.toBinaryString(word);
while (stringWord.length() < 32) // ensure that length of word is 32
stringWord = "0" + stringWord;
Integer.toBinaryString will convert an int to its binary representation as a string.
It does not give you leading zeroes, so if you really need the string to have those and be 16 bits, you can just add them yourself.
You should know how to do that.
Do note that an int is actually 32 bits in Java. You should also know how two's complement works. The binary representation of -1, for example, is 32 1s.
In terms of an algorithm to convert base10 numbers to binary, I personally think the following is pretty straightforward:
char[] array;
for (i; i < 16; i++)
{
if (yourNumber % 2 == 0)
array[16-i] = '0';
else if (yourNumber % 2 == 1)
array[16-i] = '1';
yourNumber = yourNumber / 2;
}
You can then convert your char array to a String if you like.
if you want the binary representation of a long, then there is a method in the Long objet to do so :
String Long.toString(long i, int radix);
with a radix of 2, you should have a binary representation.
regards
Guillaume
Binary is a representation and not a format to convert an integer to. For example, if you have an integer:
int i = 2;
The binary representation will be 00000010. Java has only signed integers, so this link will be helpful.