Converting between decimal and hexadecimal in Java - java

Let's say I have a byte value of 0x12 (decimal 18). I need to convert this into decimal 12. Here's how I do it:
byte hex = 0x12;
byte dec = Byte.parseByte(String.format("%2x", hex));
System.out.println(dec); // 12
Is there a better way of doing this (for example without using strings and parsing)?

Try this:
byte dec = (byte)(hex / 16 * 10 + hex % 16);
Note that it assumes that the original input is a valid BCD encoding.

If you want to iterpret a Binary coded decimal and convert to binary, you can convert to a hexadecimal string and interpret as a decimal string. Your method is a perfectly valid way to do it (be careful, though, that bytes are signed in Java.
Read up on Binary Coded Decimals here: http://en.wikipedia.org/wiki/Binary-coded_decimal
If you want to avoid string conversion, you could separate each nibble (four bytes) and multiply by the correct value:
byte hex = 0x12;
if( hex&15 > 9 || hex>>>4 > 9)
throw new NumberFormatException(); //check for valid input
byte dec = (byte)((hex & 15) + 10*(hex>>>4 & 15));
If your input is wider than a byte, this method gets out of hand easily as you need to handle each nibble separately.

Hex to Decimal - Integer.parseInt(str,16)
Decimal to Hex - Integer.toHexString(195012)

Related

Character strings to binary string - why are some characters multi-byte?

This code is supposed to convert a character strings to binary ones, but with a few strings, it returns a String with 16 binary digits, not 8 as I expected them to be.
public class aaa {
public static void main(String argv[]){
String nux="ª";
String nux2="Ø";
String nux3="(";
byte []bites = nux.getBytes();
byte []bites2 = nux2.getBytes();
byte []bites3 = nux3.getBytes();
System.out.println(AsciiToBinary(nux));
System.out.println(AsciiToBinary(nux2));
System.out.println(AsciiToBinary(nux3));
System.out.println("number of bytes :"+bites.length);
System.out.println("number of bytes :"+bites2.length);
System.out.println("number of bytes :"+bites3.length);
}
public static String AsciiToBinary(String asciiString){
byte[] bytes = asciiString.getBytes();
StringBuilder binary = new StringBuilder();
for (byte b : bytes)
{
int val = b;
for (int i = 0; i < 8; i++)
{
binary.append((val & 128) == 0 ? 0 : 1);
val <<= 1;
}
binary.append(' ');
}
return binary.toString();
}
}
in the first two strings, I don't understand why they return 2 bytes, since they are single-character strings.
Compiled here to: https://ideone.com/AbxBZ9
This returns:
11000010 10101010
11000011 10011000
00101000
number of bytes :2
number of bytes :2
number of bytes :1
I am using this code: Convert A String (like testing123) To Binary In Java
NetBeans IDE 8.1
A character is not always 1-byte long. Think about it - many languages, such as Chinese or Japanese, have thousands of characters, how would you map those characters to bytes?
You are using UTF-8 (one of the many, many ways of mapping characters to bytes) - looking up a character table for UTF-8, and searching for the sequence 11000010 10101010, I arrive at
U+00AA ª 11000010 10101010
Which is the UTF-8 encoding for ª. UTF-8 is often the default character encoding (charset) for Java -- but you cannot rely on this. That is why you should always specify a charset when converting strings to bytes or vice-versa
you can understand why some character are two bytes by running this simple code
// integer - binary
System.out.println(Byte.MIN_VALUE);
// -128 - 0b11111111111111111111111110000000
System.out.println(Byte.MAX_VALUE);
// 127 - 0b1111111
System.out.println((int) Character.MIN_VALUE);
// 0 - 0b0
System.out.println((int) Character.MAX_VALUE);
// 65535 - 0b1111111111111111
as you can see ,we can show Byte.MAX_VALUE with just 7 bits or 1 byte (01111111)
if you cast Character.MIN_VALUE to integer, it will be : 0
we can show it's binary format with one bit or 1 byte (00000000)!
but what about Character.MAX_VALUE ?
in binary format it's
1111111111111111 which is 65535 in decimal format and can be shown with 2 bytes (11111111 11111111).
so characters which their decimal format is between 0 and 65535 can be shown with 1 or 2 bytes.
hope you understand.

Java Integer.parseInt() for 32-bit signed binary string throws NumberFormatException

Is this Java Api's bug?
int i = 0xD3951892;
System.out.println(i); // -745203566
String binString = Integer.toBinaryString(i);
int radix = 2;
int j = Integer.valueOf(binString, radix );
Assertions.assertThat(j).isEqualTo(i);
I expect it to be true without any question. But it throws below exception:
java.lang.NumberFormatException: For input string: "11010011100101010001100010010010"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:495)
at java.lang.Integer.valueOf(Integer.java:556)
at com.zhugw.temp.IntegerTest.test_valueof_binary_string(IntegerTest.java:14)
So if I have a binary String , e.g. 11010011100101010001100010010010, How can I get its decimal number(-745203566) in Java? DIY? Write code to implement below equation?
Integer.valueOf(String, int radix) and Integer.parseInt(String, int radix) will only parse numbers of value -2 147 483 648 to 2 147 483 647, i.e. the values of 32-bit signed integers.
These functions cannot interpret two's complement numbers for binary (radix = 2), because the string being passed can be of any length, and so a leading 1 could be part of the number or the sign bit. I guess Java's developers decided that the most logical way to proceed is to never accept two's complement, rather than assume that a 32nd bit is a sign bit.
They read your input binary string as unsigned 3 549 763 730 (bigger than max int value). To read a negative value, you'd want to give a positive binary number with a - sign in front. For example for -5:
Integer.parseInt("1011", 2); // 11
// Even if you extended the 1s to try and make two's complement of 5,
// it would always read it as a positive binary value
Integer.parseInt("-101", 2); // -5, this is right
Solutions:
I suggest, first, that if you can store it as a positive number with extra sign information on your own (e.g. a - symbol), do that. For example:
String binString;
if(i < 0)
binString = "-" + Integer.toBinaryString(-i);
else // positive i
binString = Integer.toBinaryString(i);
If you need to use signed binary strings, in order to take a negative number in binary two's complement form (as a string) and parse it to an int, I suggest you take the two's complement manually, convert that into int, and then correct the sign. Recall that two's complement = one's complement + 1, and one's complement is just reverse each bit.
As an example implementation:
String binString = "11010011100101010001100010010010";
StringBuilder onesComplementBuilder = new StringBuilder();
for(char bit : binString.toCharArray()) {
// if bit is '0', append a 1. if bit is '1', append a 0.
onesComplementBuilder.append((bit == '0') ? 1 : 0);
}
String onesComplement = onesComplementBuilder.toString();
System.out.println(onesComplement); // should be the NOT of binString
int converted = Integer.valueOf(onesComplement, 2);
// two's complement = one's complement + 1. This is the positive value
// of our original binary string, so make it negative again.
int value = -(converted + 1);
You could also write your own version of Integer.parseInt for 32-bit two's complement binary numbers. This, of course, assumes you're not using Java 8 and can't just use Integer.parseUnsignedInt, which #llogiq pointed out while I was typing this.
EDIT: You could also use Long.parseLong(String, 2) first, then calculate the two's complement (and mask it by 0xFFFFFFFF), then downgrade the long down to int. Faster to write, probably faster code.
The API docs for Integer.toBinaryString(..) explicitly state:
The value of the argument can be recovered from the returned string s by calling Integer.parseUnsignedInt(s, 8).
(as of Java 8u25) I think this is a documentation error, and it should read Integer.parseUnsignedInt(s, 2). Note the Unsigned. This is because the toBinaryString output will include the sign bit.
Edit: Note that even though this looks like it would produce an unsigned value, it isn't. This is because Java does not really have a notion of unsigned values, only a few static methods to work with ints as if they were unsigned.

Java, Long.parse binary String

Why does this code throw a NumberFormatException :
String binStr = "1000000000000000000000000000000000000000000000000000000000000000";
System.out.println(binStr.length());// = 64
System.out.println(Long.parseLong(binStr, 2));
1000000000000000000000000000000000000000000000000000000000000000 is larger than Long.MAX_VALUE.
See https://stackoverflow.com/a/8888969/597657
Consider using BigInteger(String val, int radix) instead.
EDIT:
OK, this is new for me. It appears that Integer.parseInt(binaryIntegerString, 2) and Long.parseLong(binaryLongString, 2) parse binary as sign-magnitude not as a 2's-complement.
Because it's out of range. 1000...000 is 263, but Long only goes up to 263 - 1.
This is the same for all of Long, Integer, Short and Byte. I'll explain with a Byte example because it's readable:
System.out.println(Byte.MIN_VALUE); // -128
System.out.println(Byte.MAX_VALUE); // 127
String positive = "1000000"; // 8 binary digits, +128
String negative = "-1000000"; // 8 binary digits, -128
String plus = "+1000000"; // 8 binary digits, +128
Byte.parseByte(positive, 2); //will fail because it's bigger than Byte.MAX_VALUE
Byte.parseByte(negative, 2); //won't fail. It will return Byte.MIN_VALUE
Byte.parseByte(plus, 2); //will fail because its bigger than Byte.MAX_VALUE
The digits are interpreted unsigned, no matter what radix is provided. If you want a negative value, you have to have the minus sign at the beginning of the String. JavaDoc says:
Parses the string argument as a signed long in the radix specified by
the second argument. The characters in the string must all be digits
of the specified radix (as determined by whether Character.digit(char, int) returns a nonnegative value), except that the first character may
be an ASCII minus sign '-' ('\u002D') to indicate a negative value or
an ASCII plus sign '+' ('\u002B') to indicate a positive value. The
resulting long value is returned.
In order to get MAX_VALUE we need:
String max = "1111111"; // 7 binary digits, +127
// or
String max2 = "+1111111"; // 7 binary digits, +127
Largest long value is actually:
0111111111111111111111111111111111111111111111111111111111111111b = 9223372036854775807
This is because Long.parseLong cannot parse two's complement representation. The only way to parse two's complement binary string representation in Java SE is BigInteger:
long l = new BigInteger("1000000000000000000000000000000000000000000000000000000000000000", 2).longValue()
this gives expected -9223372036854775808result
This is the largest possible long (9223372036854775807 = 2 exp 63 - 1) in binary format. Note the L at the end of the last digit.
long largestLong = 0B0111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L;
Actually, this is works for me:
String bitStr = "-1000000000000000000000000000000000000000000000000000000000000000";
System.out.println(Long.parseLong(bitStr, 2));
Here is a thing: inside Long.parseLong() code logic is looking for explicit sign first. And respectively to the sign, different limits are used (Long.MAX_VALUE for positive, and Long.MIN_VALUE for negative binary literals). Probably it would be better if this logic looked up first to the eldest bit (0 for positive and 1 for negative numbers) the sign

why Java throws a NumberFormatException

I got an exception while parsing a string to byte
String Str ="9B7D2C34A366BF890C730641E6CECF6F";
String [] st=Str.split("(?<=\\G.{2})");
byte[]bytes = new byte[st.length];
for (int i = 0; i <st.length; i++) {
bytes[i] = Byte.parseByte(st[i]);
}
That's because the default parse method expects a number in decimal format, to parse hexadecimal number, use this parse:
Byte.parseByte(st[i], 16);
Where 16 is the base for the parsing.
As for your comment, you are right. The maximum value of Byte is 0x7F. So you can parse it as int and perform binary AND operation with 0xff to get the LSB, which is your byte:
bytes[i] = Integer.parseInt(st[i], 16) & 0xFF;
Assuming you want to parse the string as hexadecimal, try this:
bytes[i] = Byte.parseByte(st[i], 16);
The default radix is 10, and obviously B is not a base-10-digit.
Java is very picky on signedness, it will not accept values to overflow. Thus, if you parse a Byte and it is larger than 127 (for example, 130 dec or 83 hex) you will get a NumberFormatException. Same happens if you parse an 8 digit hex number as an Integer (or a 16 digit hex number as a Long) and it starts with 8-F. Such values will not be interpreted as negative (two's complement) but as illegal.
If you think that this is anal retentive, I totally agree. But that's Java style.
To parse hex values as two's complement numbers either use a large enough integer type (for example, if you are parsing a Byte use Integer instead and type cast it to a byte later) or -- if you need to parse a Long, split the number in half it is 16 digits, then combine. Here's an example:
public static long longFromHex(String s) throws IllegalArgumentException {
if (s.length() == 16)
return (Long.parseLong(s.substring(0,8),16)<<32)|(Long.parseLong(s.substring(8,16),16)&0xffffffffL);
return Long.parseLong(s, 16);
}
Or, to read a Byte, just use Integer instead:
public static byte byteFromHex(String s) throws IllegalArgumentException {
int i = Integer.parseInt(s, 16);
if (i < 0 || i > 255) throw new IllegalArgumentException("input string "+s+" does not fit into a Byte");
return (byte)i;
}

Hex representaion of bytes in a String

I read somewhere that string 0123456789ABCDEFFEDCBA987654321089ABCDEF01234567 is 192 bit (24). Its written that it is a "hex representation of bytes"
I need help on this concept.
PS: This is secret key of TripleDES algorithm.
In hexadecimal numbers, you have 16 different digits. These are written using first the ordinary 10 symbols used for decimal digits, 0 through 9. Then the first six letters of the Latin alphabet are used, i.e. A through F.
Since each digit represents a value in the range 0 through F, i.e. one of sixteen possibilities, it holds four bits of information. Thus, in a long string of hex digits, you can compute the total number of bits of information as just four times the number of digits present.
Your example string, "0123456789ABCDEFFEDCBA987654321089ABCDEF01234567", is 48 digits. This means it is a 48 * 4 = 192 bit number, in hexadecimal form.
If you're interested in viewing this large number as a sequence of bytes, just take pairs of digits, since each byte is 8 bits. The first (counting from the left) few bytes then become 0x01, 0x23, 0x45, and so on.
It's just a big number. The only difference between the numbers you are used to (such as "192") is that it's written in using the hexadecimal number system instead of the decimal number system. The hexadecimal number system uses 16 digits (0-9 and A-F) instead of the 10 you are used to (0-9).
That particular number is equivalent to 27898229935051914480226618602452055732231960245295072615 in decimal notation.
Joachim already explained the theoretical concept. If you want to play around with such numbers yourself in Java, then take a look at java.math.BigInteger.
E.g., to convert your hexadecimal number to the decimal or any other system:
// the "radix" is 16 because the string represents a hexadecimal number
BigInteger bi = new BigInteger(
"0123456789ABCDEFFEDCBA987654321089ABCDEF01234567", 16);
// print the number in decimal (digits 0-9)
System.out.println(bi.toString(10));
// print the number in octal (digits 0-7)
System.out.println(bi.toString(8));
0123456789ABCDEF FEDCBA9876543210 89ABCDEF01234567
3 (hex) keys aka 3 * 8 bytes or3 * 8 * 8 = 192 bits.
Each character in hexadecimal corresponds to 4 bits. So for your example, there are 48 characters and 48 * 4 = 192 bits.
With regard to your second question: How does the JVM distinguish between numbers in different bases?
It does not and can not do so! You as a programmer have to support the JVM. If you specify small constants, then you use a prefix to signal either decimal, octal, or hexadecimal base:
// A leading zero signals a constant in octal base;
// octal 46 is decimal 38
final int n1 = 046;
// A leading "0x" signals a constant in hexadecimal base;
// hex 3f is decimal 63
final int n2 = 0x3f;
// No prefix refers to a regular decimal number
final int n3 = 12;
There are only prefixes for octal, decimal, and hexadecimal base because these are most often used by programmers. Note, that there is no prefix for binary constants!
If you use the java.math.BigInteger class like I did in my previous reply to your first question, then you have to specify the base in the constructor:
// input numbers in octal, decimal, and hexadecimal;
// Java needs your help to recognize the base!
final BigInteger b8 = new BigInteger("12345", 8);
final BigInteger b10 = new BigInteger("12345", 10);
final BigInteger b16 = new BigInteger("12345", 16);
// output them in decimal system
System.out.println(b8.toString()); // prints "5349"
System.out.println(b10.toString()); // prints "12345"
System.out.println(b16.toString()); // prints "74565"

Categories