Link to home
Start Free TrialLog in
Avatar of krakatoa
krakatoaFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Return value from Integer.toBinaryString().

Can I just double-check this please : 


Integer.toBinaryString(int i)

Open in new window

returns an unsigned string version of the binary of the int.


My question is - how does this marry with the two's complement prescription, that a 1 in the MSB indicates a negative value? (.toBinaryString(200) for example returns 11001000).

Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

My question is - how does this marry with the two's complement prescription, that a 1 in the MSB indicates a negative value?

Try ensuring that bit is present in the argument, e.g. 0xFF00FF00 and see what happens

(.toBinaryString(200) for example returns 11001000).
That's actually a low value. Only negative when a byte type
Avatar of krakatoa

ASKER

For decimal 2147483647 toBinaryString returns :

1111111111111111111111111111111

.  . . and for decimal -2147483648 :

10000000000000000000000000000000

Yes, so you can see from the latter that 2's complement is being used
SOLUTION
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of dpearson
dpearson

Yeah I agree - it's odd that there's not an option to include the leading zeros if you want.

You may want to explore:
https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#numberOfLeadingZeros-int-
which returns the number of leading zeros.

Or you could use String format to fix this up:

	private String convertToBinary(int value) {
		int length = 32 ;
		String binary = String.format("%" + length + "s", Integer.toBinaryString(value)).replace(' ', '0');
		return binary ;
	}

Open in new window


Decimal 64  is 00000000000000000000000001000000
Decimal 123 is 00000000000000000000000001111011
Decimal -1  is 11111111111111111111111111111111
Decimal 1   is 00000000000000000000000000000001

Open in new window

Yes indeed. I wrote my own toBinaryString/toHexString routines, what must be, by now, decades ago, for this reason
If you peek inside the JDK - you can actually see the moment where this happens:

    public static String toBinaryString(int i) {
        return toUnsignedString0(i, 1);
    }

    /**
     * Convert the integer to an unsigned number.
     */
    private static String toUnsignedString0(int val, int shift) {
        // assert shift > 0 && shift <=5 : "Illegal shift value";
        int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
        int chars = Math.max(((mag + (shift - 1)) / shift), 1);                  // CUT OFF THE LEADING ZEROS
        if (COMPACT_STRINGS) {
            byte[] buf = new byte[chars];
            formatUnsignedInt(val, shift, buf, chars);
            return new String(buf, LATIN1);

Open in new window


If they had skipped that step or made it optional, you'd get what you want.
But formatUnsignedInt() is a private method - no way to just call to it.

Thnx both.

Well I'm looking to find a way to include negative numbers in this piece of code (which works fine for positives). (Of course it won't even enter the loop, when fed a negative decimal, but attempts made to get around that have also failed)

I'm wondering if I'm overlooking something simpler that I've missed in my approach. /)

class ToBinary {

  static StringBuilder binaryString;
  static int decimal;
  static int remainder;

    public static void main(String[] args){
    
        
        decimal = Integer.valueOf(args[0]).intValue();
        binaryString = new StringBuilder();
        
        
        while(decimal>0){
                
            remainder = (decimal - ((decimal/2)*2));
            binaryString.append(Integer.toString(remainder));
            decimal /=2;
            
        }
        
       
        System.out.println("\nHomemade calc ...");
        System.out.println(binaryString.reverse().toString());
        System.out.println("\n.toBinaryString() calc ...");
        System.out.println(Integer.toBinaryString(Integer.valueOf(args[0])));
        
        
    }


}

Open in new window

Doug - Just missed your last. Dealing tomo . . . late here. Thnx.
Maybe I should have posted a bit more too, since this is how the JDK does what you're working on:

    /**
     * Format an {@code int} (treated as unsigned) into a byte buffer (LATIN1 version). If
     * {@code len} exceeds the formatted ASCII representation of {@code val},
     * {@code buf} will be padded with leading zeroes.
     *
     * @param val the unsigned int to format
     * @param shift the log2 of the base to format in (4 for hex, 3 for octal, 1 for binary)    // NOTE: Just 1 for binary here
     * @param buf the byte buffer to write to
     * @param len the number of characters to write
     */
    private static void formatUnsignedInt(int val, int shift, byte[] buf, int len) {
        int charPos = len;
        int radix = 1 << shift;
        int mask = radix - 1;
        do {
            buf[--charPos] = (byte)Integer.digits[val & mask];
            val >>>= shift;
        } while (charPos > 0);
    }

    /**
     * All possible chars for representing a number as a String
     */
    static final char[] digits = {
        '0' , '1' , '2' , '3' , '4' , '5' ,
        '6' , '7' , '8' , '9' , 'a' , 'b' ,
        'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
        'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
        'o' , 'p' , 'q' , 'r' , 's' , 't' ,
        'u' , 'v' , 'w' , 'x' , 'y' , 'z'
    };

Open in new window


This writes to a byte[] buf, but could just as easily be appending to a StringBuffer.

Doug
Long and Widening Road . . . . but . . . . . .  I'm still on it.
I'm still rummaging around in the long grass on this  . . . but before I get back properly with an additional point or two, I wanted to see what you thought of this contribution to the science :

Binary Addition Example

Obtaining 38 as the result there is obviously correct when as demonstrated, 0011010 is added to 0001100. But applying that very same method when adding 39 to 51 (binary 100111 and 110011) will not be correct, giving 88 (1011000b) as an answer, instead of the correct 90 (1011010b).

When there's a carry, and the present bits are both set to 1s, the exercise at that index should be a retained 1 bit, plus a further 1 carry to the next index point. That's where they've lost the 2 difference between 88 ad 90. Isn't it ?
It's very easy to get your carrying wrong! This is how I saw it:

User generated image

And given that binary arithmetic is nearly always being done on numbers with a fixed size (number of bits) what to do about the carry when it exceeds the space is an endless challenge.


Hence the whole series of bugs around "add 1" to 255 and result is -127.


I wonder how many early rockets blew up because of that one :)

Great comment there Doug, i.i.m.s.s. ;)

But the way they've explained it on that link leaves a kind of unexplained "ghost logic", which you have to figure out without any further help from the example.

The only way that this works for someone  like me is to re-write the truth table to become :

1 + 1 = 0 carry 1
1+1+1 = 1 carry 1
1+0 = 1
0+1 = 1
0+0 = 0

Which - for me - is about a million times easier to follow.

I decided to try converting any in-range denary int to binary, by way of dealing with the ten's position each time, then adding them together. Quite a bit of fun. Only works for positive ints, this code.

class DecToBin {

  static int originalDecimal;
  static int currentDecimal;
  static int remainder ;
  static int amountToDealWith ;
  
  static int multiplier;
  static int position=0;
  static int indexPointValue = 0; 
  static int carry = 0;
  
  static String originalInBinary;
  

    public static void main(String[] args){
           
        originalDecimal = Integer.valueOf(args[0]).intValue();
        
         if(originalDecimal<10||originalDecimal%2==0){originalInBinary = String.format("%0" + ((int)Math.ceil(Math.log10(originalDecimal)/Math.log10(2))+1) + "d", 0);}       
         else{originalInBinary = String.format("%0" + ((int)Math.ceil(Math.log10(originalDecimal)/Math.log10(2))) + "d", 0);}
                
        currentDecimal = originalDecimal;     
        remainder = originalDecimal; 
        
        StringBuilder thisString = new StringBuilder(originalInBinary);
        StringBuilder accumulator = new StringBuilder(originalInBinary); //My end value container.
                 
         while(remainder>0){
         
         
                    multiplier = (int) ((currentDecimal / (int)(Math.pow(10,(int)Math.log10(currentDecimal)))));
         
            
                    remainder = (int)(currentDecimal % Math.pow(10,(int)Math.log10(currentDecimal)));
            
            
                    amountToDealWith = multiplier*((int)Math.pow(10,(int)Math.log10(currentDecimal))) ;
           
                    
                    currentDecimal = remainder ;
                      
                                    
                while(amountToDealWith>0){
                
                
                        position = (int)(Math.log10(amountToDealWith)/Math.log10(2));
                        thisString.setCharAt(position, '1');
                        amountToDealWith -= (int)Math.pow(2, position);
                                                    
                   
                }
                      
                        
                thisString.reverse(); // Correct the endianness.
            
                for(int d = originalInBinary.length()-1;d>-1;d--){ //Add the present binary part to the overall accumulation.
                       
            
                    indexPointValue = ((Character.getNumericValue(thisString.charAt(d))) + ((Character.getNumericValue(accumulator.charAt(d))))) + carry ;
               
                                                
                    switch (indexPointValue) {
                
                        case  0 : accumulator.setCharAt(d,'0') ;
                              carry = 0 ;
                        break;
                    
                        case  1 :  accumulator.setCharAt(d,'1') ;
                               carry = 0 ;
                        break;
                    
                        case  2 :  accumulator.setCharAt(d,'0') ;
                               carry = 1 ;
                        break;
                    
                        case  3 :  accumulator.setCharAt(d,'1') ;
                               carry = 1 ;
                        break;
                          
                
                    }
                                
                                                          
                }
            
                thisString = new StringBuilder(originalInBinary); // Reset to all zeros.
               
         } //End main algo.
         
                             
         if(accumulator.charAt(0)=='0'){accumulator.deleteCharAt(0);}
         
         System.out.println(originalDecimal+" in binary by this code is "+accumulator.toString());
         System.out.println("Java's Integer.toBinaryString() version is "+Integer.toBinaryString(Integer.valueOf(args[0])));
                    
         if(accumulator.toString().equals(Integer.toBinaryString(Integer.valueOf(args[0])))){System.out.println("The two binary strings match.");}
         
    }

}

Open in new window

When the JVM / OS accesses memory - or the registers - how does it interpret the state of the charge on the MSB cell, given that if the charge is on, then the cell is effectively a "1", and if it's off, then there's no charge, and it represents a "0". ?

Is the width of the array the only way that can be overcome? If so, why can't .toBinaryString(int) give the leading zeros . . .
If so, why can't .toBinaryString(int) give the leading zeros . . .
You described the (very) low level stuff in its essence but the 'leading zeros' thing has a much simpler explanation: it's the difference between visualizing a pattern across the full bit range and rendering as a number. They opted for the latter, and leading zeros have no meaning or function in numbers
When I implement Doug's examples (plus my one extra for -123) using .toBinaryString(), this emerges :

 Decimal 64  is 1000000
 Decimal 123 is 1111011
 Decimal -1  is 11111111111111111111111111111111
 Decimal 1   is 1
Decimal -123 is 11111111111111111111111110000101

The first two are 7-bits long. The next 32 bits, then bitlength 1, and finally bitlength 32 again.

So I ask myself, where's my 32-bits-worth of real estate disappeared to now? Why's it not turned out like Doug's post of :
Decimal 1   is 00000000000000000000000000000001 ?

After all, toBinaryString() is only sending back a String - why doesn't it get artificially padded at least. It's not as if you can operationally use it for anything?
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I hesitate to make an additional comment really, as it will not affect the way things happen, but as Doug has said, it all comes down to the number of bits anyone is talking about at the time - which is, almost comically, a stunningly facile explanation for what lies behind the apparent difficulty.

But . . . int, and Integer, are defined in 32 bits - a Dword.

The number of bits required (in an initial 1s or 0s String mask) to represent any particular, negative int/Integer, is * :

((int)Math.ceil(Math.log10(your_Negative_Int)/Math.log10(2))+1)//ALL 1s

Open in new window



The number of bits required to represent any particular positive int is ** :

((int)Math.ceil(Math.log10(your_Positive_Int)/Math.log10(2)))//ALL 0s

Open in new window


These two *-starred indexes then identify the righthandmost starting positions for the commencement of the actual payload of the decimal int given. This ends up in a full 32-bit representation of all in-range ints, with all leading 1s (and more importantly, leading 0s for positive numbers) as a String.

(I'm not going to go anywhere near BigInteger, fear not. Nor what might be going on in the 64-bit quadword world if it's even a question that it might need to ' get its head around ' binary maths).

One more thought, sorry, now : OK the memory cells and their charges can only represent a positive - on or off. (You can't 'prove' a negative, so the no-charge cell is muted). But the Runtime knows the length of the allocated cells for the var (int) in question. So what's stopping the runtime using the state of the cell at the base of the offset being read?
So what's stopping the runtime using the state of the cell at the base of the offset being read?
Do  you mean "Why are there no leading zeros?"
Do  you mean "Why are there no leading zeros?"

Well, I shalln't say 'yes' to that, because you've already answered it I think, saying that leading zeros are meaningless in a number, which is entirely correct.

But in a String representation of the leading zeros (which is I guess what our whole convo is about), why isn't it possible for the allocated int space in memory (I guess the int is held on the stack, but an Integer is held in RAM I assume) which has a known starting and ending offset, to be interrogated at (endianness correctly) index "leftmost" for a 0 or a 1 - charge or no charge? The runtime must know what's going on at each index, so however long the bitcount is, the start of its offset must be a readable value? Or am I going crazy?
It's always possible to query the bits of the numeric primitive you're using
Well, given that Java won't let you deal with anything smaller than a byte - let alone a single bit -  it's a big challenge to see how it's possible to query individual bits in a primitive, and if possible could you kindly indicate how you'd go about that?

There's this array of method accessors which don't seem to get anywhere close to returning a result of that sort, or allowing some further queryable token to be returned that can allow it to be done -:

int ii = -1;
    System.out.println("The binary string for "+ii+" is "+Integer.toBinaryString(ii));

    System.out.println("Length of binary for  "+ii+" is "+Integer.toBinaryString(ii).length());

    System.out.println("Leading 0s in "+ii+" is " +Integer.numberOfLeadingZeros(ii));

    System.out.println("Trailing 0s in "+ii+" is " +Integer.numberOfTrailingZeros(ii));
    
    System.out.println("Bitcount for "+ii+" is " +Integer.bitCount(ii));
    
    System.out.println("Radix 2 string rep for "+ii+" is " +Integer.toString(ii,2));
    
    System.out.println("Unsigned Radix 2 string rep for "+ii+" is " +Integer.toUnsignedString(ii,2));
    
    System.out.println("SIZE of "+ii+" is " +((Integer)ii).SIZE);
    
    System.out.println("BYTES of "+ii+" is " +((Integer)ii).BYTES);

Open in new window

Something like
    public static String toBinaryString(short n) {
        char[] a = new char[16];
        Arrays.fill(a, '0');
        StringBuilder sb = new StringBuilder(new String(a));

        for (int bit = 0; bit < 16; bit++) {
            if (((n >> bit) & 1) > 0) {
                sb.setCharAt(15 - bit, '1');
            }
        }

        return sb.toString();
    }

Open in new window

OK, but I paused to try to see why that code produces a leading 0 MSB string when dealing with a negative input , but I can't. (Also, why is it taking a short when the infamous .toBinaryString works on an int)? Thnx.
Sorry - sloppy off-by-one error. Now corrected.

All primitives have toBinaryString, that just happened to be for short
All primitives have toBinaryString, that just happened to be for short

I never realised that primitives themselves had any methods. Even Short doesn't have a toBinaryString() method afaics . . .
Sorry - I phrased that badly. I meant that I originally (in my own routines) overloaded the toBinaryString method for all primitives. Yes, in the JDK, some, but not all of, wrapper classes have these methods
On a 'personal' level (if there is even any such thing in the context), it's disappointing that whilst the OS and the JVM together can get oversight of the binary condition of any part of memory, that the system can't disclose - on a 'read-only' basis if nothing else - what .toBinaryString() superficially promises. All it succeeds in doing is to fall short of expectations.

Mind you, there are so many deprecated methods already, maybe they also caused some early rocket misadventures.

All that I've got to do now is to get why "static final char[] digits = {" in Doug's earlier comment is applicable.

Don't quite follow you there.

As for Doug's thing:
All possible chars for representing a number as a String
I think the number of "possible chars" is open to interpretation (e.g. character case can be involved) but certainly it allows up to 'standard' base 36 to be used
Don't quite follow you there.
NTW.. ;)