10

I am trying to fetch second digit from a long variable.

long mi = 110000000;
int firstDigit = 0;
String numStr = Long.toString(mi);
for (int i = 0; i < numStr.length(); i++) {
    System.out.println("" + i + " " + numStr.charAt(i));
    firstDigit =  numStr.charAt(1);
}

When I am printing firstDigit = numStr.charAt(1) on console. I am getting 1 which is expected but when the loop finishes firstDigit has 49. Little confused why.

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
shree
  • 341
  • 4
  • 13
  • 13
    Why do you need a loop if you only want the second digit? – khelwood Dec 26 '17 at 08:13
  • 2
    Second digit in which calculation system? Hexadecimal, decimal, octal, binary, ...? – quant Dec 26 '17 at 08:28
  • Related: [What does '0' do in Java?](https://stackoverflow.com/q/34409950) – Bernhard Barker Dec 26 '17 at 18:35
  • Whenever I asked a question that gets marked as duplicate, I am downvoted left, right and center (and of course down). Then, how did this one get nine upvotes? – Seshadri R Dec 28 '17 at 03:42
  • You appear to be starting with a `long` variable? Why make it into a string? – Will Crawford Dec 31 '17 at 03:16
  • Your code returns 49 because it's wrong. Once you'll have it fixed, the correct answer will be returned, and it will likely be [42](https://www.google.it/search?q=the+answer+to+life+the+universe+and+everything). – gog Mar 30 '18 at 13:15

11 Answers11

14

Because 49 is the ASCII value of char '1'.

So you should not assign a char to int directly.

And you don't need a loop here which keeps ovveriding the current value with charAt(1) anyway.

int number = numStr.charAt(1) - '0'; // substracting ASCII start value 

The above statement internally works like 49 -48 and gives you 1.

If you feel like that is confusious, as others stated use Character.getNumericValue();

Or, although I don't like ""+ hack, below should work

int secondDigit = Integer.parseInt("" + String.valueOf(mi).charAt(1));
Suresh Atta
  • 120,458
  • 37
  • 198
  • 307
10

You got confused because 49 is ASCII value of integer 1. So you may parse character to integer then you can see integer value.

    Integer.parseInt(String.valueOf(mi).charAt(1)):
pri
  • 1,521
  • 2
  • 13
  • 26
Ankit jain
  • 4,198
  • 4
  • 19
  • 24
  • 2
    to prevent `The method parseInt(String) in the type Integer is not applicable for the arguments (char)` you should use something like `Integer.parseInt(String.valueOf(mi).charAt(1)+"");` – Gholamali Irani Dec 26 '17 at 09:49
5

You're probably looking for Character.getNumericValue(...) i.e.

firstDigit = Character.getNumericValue(numStr.charAt(1));

Otherwise, as the variable firstDigit is of type int that means you're assigning the ASCII representation of the character '1' which is 49 rather than the integer at the specified index.

Also, note that since you're interested in only a particular digit there is no need to put the statement firstDigit = numStr.charAt(1); inside the loop.

rather, just do the following outside the loop.

int number = Character.getNumericValue(numStr.charAt(1));
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
3

you only need define firstDigit as a char type variable, so will print as character. since you define as int variable, it's value is the ASCII value of char '1': 49. this is why you get 49 instead of 1.

Shen Yudong
  • 1,190
  • 7
  • 14
3

the answer Integer.parseInt(String.valueOf(mi).charAt(1)+""); is correct.

However, if we want to consider performace in our program, we need some improvements.

We have to time consuming methods, Integer.parseInt() and String.valueOf(). And always a custom methods is much faster than Integer.parseInt() and String.valueOf(). see simple benchmarks.

So, high performance solution can be like below:

int y=0;
while (mi>10)
{
    y=(int) (mi%10);
    mi=mi/10;
}
System.out.println("Answer is: " + y);

to test it:

long mi=4642345432634278834L;
int y=0;

long start = System.nanoTime();

//first solution
//y=Integer.parseInt(String.valueOf(mi).charAt(1)+"");      

//seconf solution
while (mi>10)
{
    y=(int) (mi%10);
    mi=mi/10;
}

long finish = System.nanoTime();

long d = finish - start;
System.out.println("Answer is: " + y + " , Used time: " + d);

//about 821 to 1232 for while in 10 runs
//about 61225 to 76687 for parseInt in 10 runs
Gholamali Irani
  • 4,391
  • 6
  • 28
  • 59
  • If we want to consider performance we definitely should go with `(num / 10^{Log_10{num} - 1}) % 10` instead of that loop. Which is much, much faster than the other alternatives, but not exactly obvious on first glance for most people I'd wager. – Voo Dec 26 '17 at 14:26
  • There is not `^` function in java (just for Bit operation ), we should use `Pow()` method, in this case we should use `parseInt()` and other conversions. They are so time consuming in java. – Gholamali Irani Dec 26 '17 at 16:34
  • That's not supposed to be actual Java code, more LaTeX to write the mathematical formula. – Voo Dec 26 '17 at 16:36
  • 1
    @Voo Completely wrong. Log10 and/or 10^ are elegant, but numerically *extremely expensive floating-point functions*. They are also not 100% accurate because of floating-point rounding. – Thorsten S. Dec 26 '17 at 16:42
3

Doing string manipulation to work with numbers is almost always the wrong approach.

To get the second digit use the following;

int digitnum = 2;

int length = (int)Math.log10(mi));
int digit = (int)((mi/Math.pow(base,length-digitnum+1))%base);

If you want a different digit than the second change digitnum.

To avoid uncertainty with regards to floating point numbers you can use a integer math library like guavas IntMath

Taemyr
  • 3,407
  • 16
  • 26
  • While the mathematically correct answer, I'll admit that I couldn't prove that this gives the correct answer when considering float inaccuracies off the cuff. – Voo Dec 26 '17 at 14:31
  • @Voo Answer updated to just use log10, with reference to guava for pure int base alternative. – Taemyr Dec 26 '17 at 16:09
2

Let's take a look

System.out.println(numStr.charAt(1));
firstDigit =  numStr.charAt(1);
System.out.println(firstDigit);

The result wouldn't be the same you will get

1
49

This happens because your firstDigit is int. Change it to char and you will get expected result

GuessWho
  • 664
  • 1
  • 6
  • 19
1

You can also do like below,

firstDigit = Integer.parseInt( numStr.charAt(1)+"");

So it will print second digit from long number.

Sejal Rudani
  • 372
  • 4
  • 19
1

Some things which have not been mentioned yet:

  • The second digit for integer datatypes is undefined if the long number is 0-9 (No, it is not zero. Integers do not have decimal places, this is only correct for floating-point numbers. Even then you must return undefined for NaN or an infinity value). In this case you should return a sentinel like e.g. -1 to indicate that there is no second digit.

  • Using log10 to get specific digits looks elegant, but they are 1. one of the numerically most expensive functions and 2. do often give incorrect results in edge cases. I will give some counterexamples later.

Performance could be improved further:

public static int getSecondDigit(long value) {
long tmp = value >= 0 ? value : -value;
if (tmp < 10) {
  return -1;
}

long    bigNumber = 1000000000000000000L;
boolean isBig     = value >= bigNumber;

long  decrement   = isBig ? 100000000000000000L : 1;
long  firstDigit  = isBig ? bigNumber : 10;
int   result      = 0;
if (!isBig) {
  long test = 100;

  while (true) {
    if (test > value) {
      break;
    }
    decrement = firstDigit;
    firstDigit = test;
    test *= 10;
  }
}



// Remove first
while (tmp >= firstDigit) {
  tmp -= firstDigit;
}

// Count second
while (tmp >= decrement) {
  tmp -= decrement;
  result++;
}

return result;
}

Comparison: 1 000 000 random longs

String.valueOf()/Character.getNumericValue(): 106 ms
Log/Pow by Taemyr: 151 ms
Div10 by @Gholamali-Irani: 45 ms
Routine above: 30 ms

This is not the end, it can be even faster by lookup tables decrementing 1/2/4/8, 10/20/40/80 and avoid the use of multiplication.

Gholamali Irani
  • 4,391
  • 6
  • 28
  • 59
Thorsten S.
  • 4,144
  • 27
  • 41
0

try this to get second char of your long

mi.toString().charAt(1);
Sasikumar Murugesan
  • 4,412
  • 10
  • 51
  • 74
0

How to get ASCII code

int ascii = 'A';
int ascii = 'a';

So if you assign a character to an integer, the integer will be holding the ASCII value of that character. Here I explicitly gave the values, in your code you are calling a method that returns a character, that's why you are getting ASCII instead of digit.

Arun Sudhakaran
  • 2,167
  • 4
  • 27
  • 52