Definition
Binary numeric promotion follows a fixed cascade:
- Either is double? Both become double.
- Either is float? Both become float.
- Either is long? Both become long.
- Otherwise? Both become int. This means byte, short, and char are always promoted to int.
The result type is always the promoted type. There is no arithmetic that returns byte, short, or char.
Two bank tellers must do a calculation together but work in different denominations. Before adding, they convert to a single agreed denomination so the math is consistent. Small denominations (byte, short, char) always convert to the standard int denomination. If one teller works in a larger denomination (long, float, double), the other converts up to match.
Understanding the Concepts
Why does 'A' + 1 give 66?
The character 'A' has the numeric value 65. When you add an int to a char, the char is promoted to int first.
char a = 'A'; // 65
int result = a + 1; // 65 + 1 = 66 (int, NOT char)
System.out.println(result); // 66
char b = (char)(a + 1); // cast int 66 back to char
System.out.println(b); // B
Without the cast, you cannot assign an int result to a char variable because that is a narrowing conversion.
Why does the constant expression 'A' + 1 assign to char without a cast?
char b = 'A' + 1; // compiles! 66 is a constant that fits in char
This is a special rule: if the entire right side is a compile-time constant and the result fits in the target type, the compiler allows it. But if either operand is a variable, the rule does not apply:
int one = 1;
// char b = 'A' + one; // COMPILE ERROR: not a constant expression
char b = (char)('A' + one); // explicit cast needed
Why does long + float give float?
The promotion hierarchy puts float above long. This is surprising because long has 64 bits of integer precision while float has only 24 bits of mantissa.
long bigLong = 123_456_789_012_345L;
float f = 1.0f;
float result = bigLong + f; // long promoted to float first!
System.out.println(result); // 1.23456788E14 (precision lost!)
The long is converted to float before the addition. The conversion rounds the long's value to fit in float's 24-bit mantissa, losing the last digits. In banking, this is why you never mix long amounts with float arithmetic.
Why does 5 / 2 give 2 but 5 / 2.0 give 2.5?
System.out.println(5 / 2); // 2 (integer division, fraction dropped)
System.out.println(5 / 2.0); // 2.5 (int 5 promoted to double, then divided)
System.out.println(5.0 / 2); // 2.5 (same: promotion to double)
When both operands are int, the division is integer division and the remainder is dropped. When either operand is a floating-point type, the other is promoted and the division is floating-point.
In banking, this matters for interest calculations:
// WRONG: integer division gives 0
int rate = 7;
int months = 12;
int monthlyRate = rate / months; // 0 (not 0.583)
// CORRECT: force floating-point division
double monthlyRate = rate / 12.0; // 0.5833...
// BEST for money: use BigDecimal
BigDecimal monthlyRate = new BigDecimal("7").divide(new BigDecimal("12"), 6, RoundingMode.HALF_UP);
How does += hide a narrowing cast?
char c = 'A';
// c = c + 1; // COMPILE ERROR: c + 1 is int, cannot assign to char
c += 1; // compiles! equivalent to c = (char)(c + 1)
System.out.println(c); // B
byte b = 127;
b += 1; // compiles! equivalent to b = (byte)(b + 1)
System.out.println(b); // -128 (silent overflow!)
Compound operators (+=, -=, *=, etc.) insert an implicit narrowing cast to the left-hand type. This is convenient but can silently overflow.
What about String + int?
The + operator with a String converts the other operand to a String via String.valueOf(). This is string concatenation, not numeric promotion.
System.out.println("Balance: " + 1000); // "Balance: 1000"
System.out.println("Sum: " + 1 + 2); // "Sum: 12" (left to right: string + int + int)
System.out.println("Sum: " + (1 + 2)); // "Sum: 3" (parentheses force numeric first)
System.out.println(1 + 2 + " rupees"); // "3 rupees" (1+2 is numeric, then concat)
What are common mistakes?
- Assuming byte + byte returns byte. It returns int. Cast needed to store back.
- Integer division when you wanted a fraction.
7 / 12is 0. Use7.0 / 12or BigDecimal. - Relying on += without understanding the hidden cast.
byte b = 127; b += 1;wraps to -128. - Mixing long with float. Long precision is lost. Use double or BigDecimal instead.
- String + int confusion.
"1" + 2 + 3is"123", not"6". Parenthesize the numeric part.
What should a senior engineer know?
- There are no byte, short, or char arithmetic bytecodes. The JVM uses
iadd(int add) even for byte operands. Promotion to int matches the hardware reality. - Promotion applies to comparisons too. Comparing a char to an int compares their numeric values.
'A' == 65is true. - Ternary result type follows promotion rules.
condition ? byteVal : intValpromotes the byte to int. - Bitwise operators also promote.
~(byte)0xFFis an int, not a byte.
Visual Overview
Binary Numeric Promotion:
either double? → both double
either float? → both float
either long? → both long
otherwise → both int (byte/short/char promoted)
Examples:
'A' + 1 → int 65 + int 1 = int 66
long + float → float + float = float (precision loss!)
5 / 2 → int / int = int 2 (truncated)
5 / 2.0 → double / double = double 2.5
byte + byte → int + int = int
String special:
"x" + 1 + 2 → "x12" (string concat left to right)
1 + 2 + "x" → "3x" (int add first, then concat)
Key Takeaway
When the two sides of an arithmetic operator have different types, Java promotes both to a common type before computing. The minimum is always int, so byte, short, and char arithmetic always produces an int result. If either side is long, float, or double, the other is promoted to match.
This explains why 'A' + 1 gives the int 66 instead of the char 'B'. The char is promoted to int, the addition happens in int, and the result is int. To get a char back, you need an explicit cast. The compound operator c += 1 compiles without a cast because it inserts the narrowing cast automatically.
The long + float case is the trap: float outranks long in the hierarchy, so the long is converted to float first, and large longs lose precision because float has only 24 bits of mantissa. And integer division (5 / 2 = 2) versus floating-point division (5 / 2.0 = 2.5) depends entirely on whether one operand is a floating type.
One sentence to remember: Java promotes both operands to at least int and up to the widest type, so char and byte arithmetic is always int arithmetic, and long plus float is float with possible precision loss.
---