Integer Arithmetic in Java with Char and Integer Literal

Integer arithmetic in Java with char and integer literal

'a' + 10 is a compile-time constant expression with the value of 'k', which can initialise a variable of type char. This is the same as being able to assign a byte variable with a literal integer in [-128, 127]. A byte in the range of [128, 255] may be more annoying.

Java char addition (char1 = char2 + 10)

When you add numbers, they undergo binary numeric promotion. That is, the operands are widened to allow them to be added.

When you add a char to an int, the char is widened to an int, and the result is an int.

As such, you would have to cast the int back to a char:

char a = (char) (c + 10);

However, even when adding a char to another char, both are widened to int, and the result is again an int, and so cannot be assigned to a char variable. The rules are basically:

  • If either operand is a double, widen both to double
  • Else, if either operand is a float, widen both to float
  • Else, if either operand is a long, widen both to long
  • Else, widen both to int

So, even if you were adding a byte to a byte, both are widened to int, added, and the result is an int.


The exception to this is if you made c final:

final char c = 34;

In that case, c has a compile-time constant value, so c + 10 is a compile-time constant expression. Because it's a compile-time constant expression, the compiler knows its value, and knows that it fits into the range of char; so it would be allowed:

final char c = 34;
char a = c + 10;

Why int a = 5 - '0'; is possible in java?

Doc:

char: The char data type is a single 16-bit Unicode character. It has a minimum value of '\u0000' (or 0) and a maximum value of '\uffff' (or 65,535 inclusive).

In this case it is casted to int, that is why it works.

Correction
This is called Widening Primitive Conversion. Thanks @Andreas!

+1 curiosity

  public static  void main(String[] args) {
int a = 5 - '0';
}

I compiled this to bytecode:

  public static main([Ljava/lang/String;)V
L0
LINENUMBER 24 L0
BIPUSH -43
ISTORE 1
L1
LINENUMBER 25 L1
RETURN
L2
LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
LOCALVARIABLE a I L1 L2 1
MAXSTACK = 1
MAXLOCALS = 2
}

Notice the line with BIPUSH -43 which means this value is calculated buildtime not runtime!

In Java, is the result of the addition of two chars an int or a char?

The result of adding Java chars, shorts, or bytes is an int:

Java Language Specification on Binary Numeric Promotion:

  • If any of the operands is of a reference type, unboxing conversion
    (§5.1.8) is performed. Then:
  • If either operand is of type double, the
    other is converted to double.
  • Otherwise, if either operand is of type
    float, the other is converted to float.
  • Otherwise, if either operand
    is of type long, the other is converted to long.
  • Otherwise, both
    operands are converted to type int.

But note what it says about compound assignment operators (like +=):

The result of the binary operation is converted to the type of the left-hand variable ... and the result of the conversion is stored into the variable.

For example:

char x = 1, y = 2;
x = x + y; // compile error: "possible loss of precision (found int, required char)"
x = (char)(x + y); // explicit cast back to char; OK
x += y; // compound operation-assignment; also OK

One way you can find out the type of the result, in general, is to cast it to an Object and ask it what class it is:

System.out.println(((Object)('a' + 'b')).getClass());
// outputs: class java.lang.Integer

If you're interested in performance, note that the Java bytecode doesn't even have dedicated instructions for arithmetic with the smaller data types. For example, for adding, there are instructions iadd (for ints), ladd (for longs), fadd (for floats), dadd (for doubles), and that's it. To simulate x += y with the smaller types, the compiler will use iadd and then zero the upper bytes of the int using an instruction like i2c ("int to char"). If the native CPU has dedicated instructions for 1-byte or 2-byte data, it's up to the Java virtual machine to optimize for that at run time.

If you want to concatenate characters as a String rather than interpreting them as a numeric type, there are lots of ways to do that. The easiest is adding an empty String to the expression, because adding a char and a String results in a String. All of these expressions result in the String "ab":

  • 'a' + "" + 'b'
  • "" + 'a' + 'b' (this works because "" + 'a' is evaluated first; if the "" were at the end instead you would get "195")
  • new String(new char[] { 'a', 'b' })
  • new StringBuilder().append('a').append('b').toString()
  • String.format("%c%c", 'a', 'b')

Why we can not pass integer literal as argument to method with byte as formal parameter

First, Assignment Conversions, JLS 5.2 covers what values can be assigned.

In addition, if the expression is a constant expression (§15.28) of type byte, short, char, or int:

  • A narrowing primitive conversion may be used if the variable is of type byte, short, or char, and the value of the constant expression is representable in the type of the variable.

For byte a = 0; the constant expression is the int 0 which is narrowed to a byte.

Next, Invocation Contexts, JLS 5.3 covers what values can be passed to methods.

Neither strict nor loose invocation contexts include the implicit narrowing of integer constant expressions which is allowed in assignment contexts.

Therefore, your code has a compiler error for the constant expression narrowing conversion that is disallowed in an invocation context (method call).

setByte(0); // no implicit narrowing conversion, even if it's a constant expression
void setByte(byte b){}

Your code 128 - 1 is a constant expression that is narrowed to a byte.

However, 2147483648 - 1 is disallowed because 2147483648 itself is not a valid int literal, Section 3.10.1, "Integer Literals".

It is a compile-time error if the decimal literal 2147483648 appears anywhere other than as the operand of the unary minus operator; or if a decimal literal of type int is larger than 2147483648 (231).

If you really want to use an unnecessarily complicated expression to initialize an int, you can use a long literal to make the expression legal:

2147483648L - 1

but then you must explicitly cast the expression to an int; there is no implicit narrowing from anything wider than int to an int:

(int) (2147483648L - 1)

Strangely, you don't have to place parentheses around the expression so that the cast applies to the whole expression, although I strongly recommended to use parentheses for clarity.

(int) 2147483648L - 1  // It's 2147483647!

The int cast on the long literal, which is out of int range, will yield -2147483648, a valid int value. Subtracting 1 here involves overflow in the negative direction, yielding the intended value of 2147483647.



Related Topics



Leave a reply



Submit