I have two Numbers. Eg:
Number a = 2;
Number b = 3;
//Following is an error:
Number c = a + b;
Why arithmetic operations are not supported on Numbers? Anyway how would I add these two numbers in java? (Of course I'm getting them from somewhere and I don't know if they are Integer or float etc).
You say you don't know if your numbers are integer or float... when you use the Number class, the compiler also doesn't know if your numbers are integers, floats or some other thing. As a result, the basic math operators like + and - don't work; the computer wouldn't know how to handle the values.
START EDIT
Based on the discussion, I thought an example might help. Computers store floating point numbers as two parts, a coefficient and an exponent. So, in a theoretical system, 001110 might be broken up as 0011 10, or 32 = 9. But positive integers store numbers as binary, so 001110 could also mean 2 + 4 + 8 = 14. When you use the class Number, you're telling the computer you don't know if the number is a float or an int or what, so it knows it has 001110 but it doesn't know if that means 9 or 14 or some other value.
END EDIT
What you can do is make a little assumption and convert to one of the types to do the math. So you could have
Number c = a.intValue() + b.intValue();
which you might as well turn into
Integer c = a.intValue() + b.intValue();
if you're willing to suffer some rounding error, or
Float c = a.floatValue() + b.floatValue();
if you suspect that you're not dealing with integers and are okay with possible minor precision issues. Or, if you'd rather take a small performance blow instead of that error,
BigDecimal c = new BigDecimal(a.floatValue()).add(new BigDecimal(b.floatValue()));
It would also work to make a method to handle the adding for you. Now I do not know the performance impact this will cause but I assume it will be less than using BigDecimal.
public static Number addNumbers(Number a, Number b) {
if(a instanceof Double || b instanceof Double) {
return a.doubleValue() + b.doubleValue();
} else if(a instanceof Float || b instanceof Float) {
return a.floatValue() + b.floatValue();
} else if(a instanceof Long || b instanceof Long) {
return a.longValue() + b.longValue();
} else {
return a.intValue() + b.intValue();
}
}
The only way to correctly add any two types of java.lang.Number is:
Number a = 2f; // Foat
Number b = 3d; // Double
Number c = new BigDecimal( a.toString() ).add( new BigDecimal( b.toString() ) );
This works even for two arguments with a different number-type. It will (should?) not produce any sideeffects like overflows or loosing precision, as far as the toString() of the number-type does not reduce precision.
java.lang.Number is just the superclass of all wrapper classes of primitive types (see java doc). Use the appropriate primitive type (double, int, etc.) for your purpose, or the respective wrapper class (Double, Integer, etc.).
Consider this:
Number a = 1.5; // Actually Java creates a double and boxes it into a Double object
Number b = 1; // Same here for int -> Integer boxed
// What should the result be? If Number would do implicit casts,
// it would behave different from what Java usually does.
Number c = a + b;
// Now that works, and you know at first glance what that code does.
// Nice explicit casts like you usually use in Java.
// The result is of course again a double that is boxed into a Double object
Number d = a.doubleValue() + (double)b.intValue();
Use the following:
Number c = a.intValue() + b.intValue(); // Number is an object and not a primitive data type.
Or:
int a = 2;
int b = 3;
int c = 2 + 3;
I think there are 2 sides to your question.
Why is operator+ not supported on Number?
Because the Java language spec. does not specify this, and there is no operator overloading. There is also not a compile-time natural way to cast the Number to some fundamental type, and there is no natural add to define for some type of operations.
Why are basic arithmic operations not supported on Number?
(Copied from my comment:)
Not all subclasses can implement this in a way you would expect. Especially with the Atomic types it's hard to define a usefull contract for e.g. add.
Also, a method add would be trouble if you try to add a Long to a Short.
If you know the Type of one number but not the other it is possible to do something like
public Double add(Double value, Number increment) {
return value + Double.parseDouble(increment.toString());
}
But it can be messy, so be aware of potential loss of accuracy and NumberFormatExceptions
Number is an abstract class which you cannot make an instance of. Provided you have a correct instance of it, you can get number.longValue() or number.intValue() and add them.
First of all, you should be aware that Number is an abstract class. What happens here is that when you create your 2 and 3, they are interpreted as primitives and a subtype is created (I think an Integer) in that case. Because an Integer is a subtype of Number, you can assign the newly created Integer into a Number reference.
However, a number is just an abstraction. It could be integer, it could be floating point, etc., so the semantics of math operations would be ambiguous.
Number does not provide the classic map operations for two reasons:
First, member methods in Java cannot be operators. It's not C++. At best, they could provide an add()
Second, figuring out what type of operation to do when you have two inputs (e.g., a division of a float by an int) is quite tricky.
So instead, it is your responsibility to make the conversion back to the specific primitive type you are interested in it and apply the mathematical operators.
The best answer would be to make util with double dispatch drilling down to most known types (take a look at Smalltalk addtition implementation)
Related
If I am getting a random double, how to get only the int?
Examples:
1) 114.999 - get the "114" as an int
2) 565.343234 - get the "565" as an int.
Given the value :
float f = 114.999f;
int i = (int) f;
use a cast to downcast it to an int.
The simplest way would be to cast your double value to an int. For example,
(int) 114.9999 == 114
However, the double type can represent numbers beyond the range of an int. You may need to check if your double is below the smallest possible integer or beyond the largest possible integer to avoid integer overflow issues.
The easiest way to get the integer part of a floating point number would be a simple cast:
double d = 14.999;
int i = (int)d; //14
If you have a primitive wrapper like Double you can use the method intValue() that all subclasses of Number need to provide. However, since those are objects the references can be null and that has to be handled:
Double d = 14.999; //this makes use of auto-boxing
int i = d != null ? d.intValue() : 0; //here 0 is the default value if d is null
Note that this will just truncate the value which can lead to unexpected results due to precision issues, especially when calculations are involved. Due to that you could end up with a number like 14.999999999 when you'd expect 15 or something higher.
Another issue might be that you won't get the next smaller integer for negative values but the next higher, i.e. -14.999 will be truncated to -14.
You should keep that in mind and if those are issues for you have a look at the functions provided by the classes Math, BigDecimal etc.
Does Java have anything to represent infinity for every numerical data type? How is it implemented such that I can do mathematical operations with it?
E.g.
int myInf = infinity; //However it is done
myInf + 5; //returns infinity
myInf*(-1); //returns negative infinity
I have tried using very large numbers, but I want a proper, easy solution.
double supports Infinity
double inf = Double.POSITIVE_INFINITY;
System.out.println(inf + 5);
System.out.println(inf - inf); // same as Double.NaN
System.out.println(inf * -1); // same as Double.NEGATIVE_INFINITY
prints
Infinity
NaN
-Infinity
note: Infinity - Infinity is Not A Number.
I'm supposing you're using integer math for a reason. If so, you can get a result that's functionally nearly the same as POSITIVE_INFINITY by using the MAX_VALUE field of the Integer class:
Integer myInf = Integer.MAX_VALUE;
(And for NEGATIVE_INFINITY you could use MIN_VALUE.) There will of course be some functional differences, e.g., when comparing myInf to a value that happens to be MAX_VALUE: clearly this number isn't less than myInf. Also, as noted in the comments below, incrementing positive infinity will wrap you back around to negative numbers (and decrementing negative infinity will wrap you back to positive).
There's also a library that actually has fields POSITIVE_INFINITY and NEGATIVE_INFINITY, but they are really just new names for MAX_VALUE and MIN_VALUE.
To use Infinity, you can use Double which supports Infinity: -
System.out.println(Double.POSITIVE_INFINITY);
System.out.println(Double.POSITIVE_INFINITY * -1);
System.out.println(Double.NEGATIVE_INFINITY);
System.out.println(Double.POSITIVE_INFINITY - Double.NEGATIVE_INFINITY);
System.out.println(Double.POSITIVE_INFINITY - Double.POSITIVE_INFINITY);
OUTPUT: -
Infinity
-Infinity
-Infinity
Infinity
NaN
The Double and Float types have the POSITIVE_INFINITY constant.
Integer Infinity :
Integer maxNumber = Integer.MAX_VALUE
Double Infinity
Double maxNumber = Double.MAX_VALUE;
Double positiveInf = Double.POSITIVE_INFINITY;
Double negativeInf = Double.NEGATIVE_INFINITY
Float infinity
Float positiveInf = Float.POSITIVE_INFINITY;
Float negativeInf = Float.NEGATIVE_INFINITY
Float maxNumber = Float.MAX_VALUE;
I'm not sure that Java has infinity for every numerical type but for some numerical data types the answer is positive:
Float.POSITIVE_INFINITY
Float.NEGATIVE_INFINITY
or
Double.POSITIVE_INFINITY
Double.NEGATIVE_INFINITY
Also you may find useful the following article which represents some mathematical operations involving +/- infinity: Java Floating-Point Number Intricacies.
Only Double and Float type support POSITIVE_INFINITY constant.
A generic solution is to introduce a new type. It may be more involved, but it has the advantage of working for any type that doesn't define its own infinity.
If T is a type for which lteq is defined, you can define InfiniteOr<T> with lteq something like this:
class InfiniteOr with type parameter T:
field the_T of type null-or-an-actual-T
isInfinite()
return this.the_T == null
getFinite():
assert(!isInfinite());
return this.the_T
lteq(that)
if that.isInfinite()
return true
if this.isInfinite()
return false
return this.getFinite().lteq(that.getFinite())
I'll leave it to you to translate this to exact Java syntax. I hope the ideas are clear; but let me spell them out anyways.
The idea is to create a new type which has all the same values as some already existing type, plus one special value which—as far as you can tell through public methods—acts exactly the way you want infinity to act, e.g. it's greater than anything else. I'm using null to represent infinity here, since that seems the most straightforward in Java.
If you want to add arithmetic operations, decide what they should do, then implement that. It's probably simplest if you handle the infinite cases first, then reuse the existing operations on finite values of the original type.
There might or might not be a general pattern to whether or not it's beneficial to adopt a convention of handling left-hand-side infinities before right-hand-side infinities or vice versa; I can't tell without trying it out, but for less-than-or-equal (lteq) I think it's simpler to look at right-hand-side infinity first. I note that lteq is not commutative, but add and mul are; maybe that is relevant.
Note: coming up with a good definition of what should happen on infinite values is not always easy. It is for comparison, addition and multiplication, but maybe not subtraction. Also, there is a distinction between infinite cardinal and ordinal numbers which you may want to pay attention to.
For the numeric wrapper types.
e.g Double.POSITIVE_INFINITY
Hope this might help you.
Since the class Number is not final, here is an
idea, that I don't find yet in the other posts.
Namely to subclass the class Number.
This would somehow deliver an object that can be treated
as infinity for Integer, Long, Double, Float,
BigInteger and BigDecimal.
Since there are only two values, we could use the singleton pattern:
public final class Infinity extends Number {
public final static Infinity POSITIVE = new Infinity(false);
public final static Infinity NEGATIVE = new Infinity(true);
private boolean negative;
private Infinity(boolean n) {
negative = n;
}
}
Somehow I think the remaining methods intValue(), longValue()
etc.. should then be overriden to throw an exceptions. So that
the infinity value cannot be used without further precautions.
I'm a beginner in Java...
I found another implementation for the infinity in the Java documentation, for the boolean and double types.
https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.2.3
Positive zero and negative zero compare equal; thus the result of the
expression 0.0==-0.0 is true and the result of 0.0>-0.0 is false. But
other operations can distinguish positive and negative zero; for
example, 1.0/0.0 has the value positive infinity, while the value of
1.0/-0.0 is negative infinity.
It looks ugly, but it works.
public class Main {
public static void main(String[] args) {
System.out.println(1.0/0.0);
System.out.println(-1.0/0.0);
}
}
Does Java have anything to represent infinity for every numerical data type? How is it implemented such that I can do mathematical operations with it?
E.g.
int myInf = infinity; //However it is done
myInf + 5; //returns infinity
myInf*(-1); //returns negative infinity
I have tried using very large numbers, but I want a proper, easy solution.
double supports Infinity
double inf = Double.POSITIVE_INFINITY;
System.out.println(inf + 5);
System.out.println(inf - inf); // same as Double.NaN
System.out.println(inf * -1); // same as Double.NEGATIVE_INFINITY
prints
Infinity
NaN
-Infinity
note: Infinity - Infinity is Not A Number.
I'm supposing you're using integer math for a reason. If so, you can get a result that's functionally nearly the same as POSITIVE_INFINITY by using the MAX_VALUE field of the Integer class:
Integer myInf = Integer.MAX_VALUE;
(And for NEGATIVE_INFINITY you could use MIN_VALUE.) There will of course be some functional differences, e.g., when comparing myInf to a value that happens to be MAX_VALUE: clearly this number isn't less than myInf. Also, as noted in the comments below, incrementing positive infinity will wrap you back around to negative numbers (and decrementing negative infinity will wrap you back to positive).
There's also a library that actually has fields POSITIVE_INFINITY and NEGATIVE_INFINITY, but they are really just new names for MAX_VALUE and MIN_VALUE.
To use Infinity, you can use Double which supports Infinity: -
System.out.println(Double.POSITIVE_INFINITY);
System.out.println(Double.POSITIVE_INFINITY * -1);
System.out.println(Double.NEGATIVE_INFINITY);
System.out.println(Double.POSITIVE_INFINITY - Double.NEGATIVE_INFINITY);
System.out.println(Double.POSITIVE_INFINITY - Double.POSITIVE_INFINITY);
OUTPUT: -
Infinity
-Infinity
-Infinity
Infinity
NaN
The Double and Float types have the POSITIVE_INFINITY constant.
Integer Infinity :
Integer maxNumber = Integer.MAX_VALUE
Double Infinity
Double maxNumber = Double.MAX_VALUE;
Double positiveInf = Double.POSITIVE_INFINITY;
Double negativeInf = Double.NEGATIVE_INFINITY
Float infinity
Float positiveInf = Float.POSITIVE_INFINITY;
Float negativeInf = Float.NEGATIVE_INFINITY
Float maxNumber = Float.MAX_VALUE;
I'm not sure that Java has infinity for every numerical type but for some numerical data types the answer is positive:
Float.POSITIVE_INFINITY
Float.NEGATIVE_INFINITY
or
Double.POSITIVE_INFINITY
Double.NEGATIVE_INFINITY
Also you may find useful the following article which represents some mathematical operations involving +/- infinity: Java Floating-Point Number Intricacies.
Only Double and Float type support POSITIVE_INFINITY constant.
A generic solution is to introduce a new type. It may be more involved, but it has the advantage of working for any type that doesn't define its own infinity.
If T is a type for which lteq is defined, you can define InfiniteOr<T> with lteq something like this:
class InfiniteOr with type parameter T:
field the_T of type null-or-an-actual-T
isInfinite()
return this.the_T == null
getFinite():
assert(!isInfinite());
return this.the_T
lteq(that)
if that.isInfinite()
return true
if this.isInfinite()
return false
return this.getFinite().lteq(that.getFinite())
I'll leave it to you to translate this to exact Java syntax. I hope the ideas are clear; but let me spell them out anyways.
The idea is to create a new type which has all the same values as some already existing type, plus one special value which—as far as you can tell through public methods—acts exactly the way you want infinity to act, e.g. it's greater than anything else. I'm using null to represent infinity here, since that seems the most straightforward in Java.
If you want to add arithmetic operations, decide what they should do, then implement that. It's probably simplest if you handle the infinite cases first, then reuse the existing operations on finite values of the original type.
There might or might not be a general pattern to whether or not it's beneficial to adopt a convention of handling left-hand-side infinities before right-hand-side infinities or vice versa; I can't tell without trying it out, but for less-than-or-equal (lteq) I think it's simpler to look at right-hand-side infinity first. I note that lteq is not commutative, but add and mul are; maybe that is relevant.
Note: coming up with a good definition of what should happen on infinite values is not always easy. It is for comparison, addition and multiplication, but maybe not subtraction. Also, there is a distinction between infinite cardinal and ordinal numbers which you may want to pay attention to.
For the numeric wrapper types.
e.g Double.POSITIVE_INFINITY
Hope this might help you.
Since the class Number is not final, here is an
idea, that I don't find yet in the other posts.
Namely to subclass the class Number.
This would somehow deliver an object that can be treated
as infinity for Integer, Long, Double, Float,
BigInteger and BigDecimal.
Since there are only two values, we could use the singleton pattern:
public final class Infinity extends Number {
public final static Infinity POSITIVE = new Infinity(false);
public final static Infinity NEGATIVE = new Infinity(true);
private boolean negative;
private Infinity(boolean n) {
negative = n;
}
}
Somehow I think the remaining methods intValue(), longValue()
etc.. should then be overriden to throw an exceptions. So that
the infinity value cannot be used without further precautions.
I'm a beginner in Java...
I found another implementation for the infinity in the Java documentation, for the boolean and double types.
https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.2.3
Positive zero and negative zero compare equal; thus the result of the
expression 0.0==-0.0 is true and the result of 0.0>-0.0 is false. But
other operations can distinguish positive and negative zero; for
example, 1.0/0.0 has the value positive infinity, while the value of
1.0/-0.0 is negative infinity.
It looks ugly, but it works.
public class Main {
public static void main(String[] args) {
System.out.println(1.0/0.0);
System.out.println(-1.0/0.0);
}
}
I am reading a text file which contains numbers in the range [1, 10^100]. I am then performing a sequence of arithmetic operations on each number. I would like to use a BigInteger only if the number is out of the int/long range. One approach would be to count how many digits there are in the string and switch to BigInteger if there are too many. Otherwise I'd just use primitive arithmetic as it is faster. Is there a better way?
Is there any reason why Java could not do this automatically i.e. switch to BigInteger if an int was too small? This way we would not have to worry about overflows.
I suspect the decision to use primitive values for integers and reals (done for performance reasons) made that option not possible. Note that Python and Ruby both do what you ask.
In this case it may be more work to handle the smaller special case than it is worth (you need some custom class to handle the two cases), and you should just use BigInteger.
Is there any reason why Java could not do this automatically i.e. switch to BigInteger if an int was too small?
Because that is a higher level programming behavior than what Java currently is. The language is not even aware of the BigInteger class and what it does (i.e. it's not in JLS). It's only aware of Integer (among other things) for boxing and unboxing purposes.
Speaking of boxing/unboxing, an int is a primitive type; BigInteger is a reference type. You can't have a variable that can hold values of both types.
You could read the values into BigIntegers, and then convert them to longs if they're small enough.
private final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
private static List<BigInteger> readAndProcess(BufferedReader rd) throws IOException {
List<BigInteger> result = new ArrayList<BigInteger>();
for (String line; (line = rd.readLine()) != null; ) {
BigInteger bignum = new BigInteger(line);
if (bignum.compareTo(LONG_MAX) > 0) // doesn't fit in a long
result.add(bignumCalculation(bignum));
else result.add(BigInteger.valueOf(primitiveCalculation(bignum.longValue())));
}
return result;
}
private BigInteger bignumCalculation(BigInteger value) {
// perform the calculation
}
private long primitiveCalculation(long value) {
// perform the calculation
}
(You could make the return value a List<Number> and have it a mixed collection of BigInteger and Long objects, but that wouldn't look very nice and wouldn't improve performance by a lot.)
The performance may be better if a large amount of the numbers in the file are small enough to fit in a long (depending on the complexity of calculation). There's still risk for overflow depending on what you do in primitiveCalculation, and you've now repeated the code, (at least) doubling the bug potential, so you'll have to decide if the performance gain really is worth it.
If your code is anything like my example, though, you'd probably have more to gain by parallelizing the code so the calculations and the I/O aren't performed on the same thread - you'd have to do some pretty heavy calculations for an architecture like that to be CPU-bound.
The impact of using BigDecimals when something smaller will suffice is surprisingly, err, big: Running the following code
public static class MyLong {
private long l;
public MyLong(long l) { this.l = l; }
public void add(MyLong l2) { l += l2.l; }
}
public static void main(String[] args) throws Exception {
// generate lots of random numbers
long ls[] = new long[100000];
BigDecimal bds[] = new BigDecimal[100000];
MyLong mls[] = new MyLong[100000];
Random r = new Random();
for (int i=0; i<ls.length; i++) {
long n = r.nextLong();
ls[i] = n;
bds[i] = new BigDecimal(n);
mls[i] = new MyLong(n);
}
// time with longs & Bigints
long t0 = System.currentTimeMillis();
for (int j=0; j<1000; j++) for (int i=0; i<ls.length-1; i++) {
ls[i] += ls[i+1];
}
long t1 = Math.max(t0 + 1, System.currentTimeMillis());
for (int j=0; j<1000; j++) for (int i=0; i<ls.length-1; i++) {
bds[i].add(bds[i+1]);
}
long t2 = System.currentTimeMillis();
for (int j=0; j<1000; j++) for (int i=0; i<ls.length-1; i++) {
mls[i].add(mls[i+1]);
}
long t3 = System.currentTimeMillis();
// compare times
t3 -= t2;
t2 -= t1;
t1 -= t0;
DecimalFormat df = new DecimalFormat("0.00");
System.err.println("long: " + t1 + "ms, bigd: " + t2 + "ms, x"
+ df.format(t2*1.0/t1) + " more, mylong: " + t3 + "ms, x"
+ df.format(t3*1.0/t1) + " more");
}
produces, on my system, this output:
long: 375ms, bigd: 6296ms, x16.79 more, mylong: 516ms, x1.38 more
The MyLong class is there only to look at the effects of boxing, to compare against what you would get with a custom BigOrLong class.
Java is Fast--really really Fast. It's only 2-4x slower than c and sometimes as fast or a tad faster where most other languages are 10x (python) to 100x (ruby) slower than C/Java. (Fortran is also hella-fast, by the way)
Part of this is because it doesn't do things like switch number types for you. It could, but currently it can inline an operation like "a*5" in just a few bytes, imagine the hoops it would have to go through if a was an object. It would at least be a dynamic call to a's multiply method which would be a few hundred / thousand times slower than it was when a was simply an integer value.
Java probably could, these days, actually use JIT compiling to optimize the call better and inline it at runtime, but even then very few library calls support BigInteger/BigDecimal so there would be a LOT of native support, it would be a completely new language.
Also imagine how switching from int to BigInteger instead of long would make debugging video games crazy-hard! (Yeah, every time we move to the right side of the screen the game slows down by 50x, the code is all the same! How is this possible?!??)
Would it have been possible? Yes. But there are many problems with it.
Consider, for instance, that Java stores references to BigInteger, which is actually allocated on the heap, but store int literals. The difference can be made clear in C:
int i;
BigInt* bi;
Now, to automatically go from a literal to a reference, one would necessarily have to annotate the literal somehow. For instance, if the highest bit of the int was set, then the other bits could be used as a table lookup of some sort to retrieve the proper reference. That also means you'd get a BigInt** bi whenever it overflowed into that.
Of course, that's the bit usually used for sign, and hardware instructions pretty much depend on it. Worse still, if we do that, then the hardware won't be able to detect overflow and set the flags to indicate it. As a result, each operation would have to be accompanied by some test to see if and overflow has happened or will happen (depending on when it can be detected).
All that would add a lot of overhead to basic integer arithmetic, which would in practice negate any benefits you had to begin with. In other words, it is faster to assume BigInt than it is to try to use int and detect overflow conditions while at the same time juggling with the reference/literal problem.
So, to get any real advantage, one would have to use more space to represent ints. So instead of storing 32 bits in the stack, in the objects, or anywhere else we use them, we store 64 bits, for example, and use the additional 32 bits to control whether we want a reference or a literal. That could work, but there's an obvious problem with it -- space usage. :-) We might see more of it with 64 bits hardware, though.
Now, you might ask why not just 40 bits (32 bits + 1 byte) instead of 64? Basically, on modern hardware it is preferable to store stuff in 32 bits increments for performance reasons, so we'll be padding 40 bits to 64 bits anyway.
EDIT
Let's consider how one could go about doing this in C#. Now, I have no programming experience with C#, so I can't write the code to do it, but I expect I can give an overview.
The idea is to create a struct for it. It should look roughly like this:
public struct MixedInt
{
private int i;
private System.Numeric.BigInteger bi;
public MixedInt(string s)
{
bi = BigInteger.Parse(s);
if (parsed <= int.MaxValue && parsed => int.MinValue)
{
i = (int32) parsed;
bi = 0;
}
}
// Define all required operations
}
So, if the number is in the integer range we use int, otherwise we use BigInteger. The operations have to ensure transition from one to another as required/possible. From the client point of view, this is transparent. It's just one type MixedInt, and the class takes care of using whatever fits better.
Note, however, that this kind of optimization may well be part of C#'s BigInteger already, given it's implementation as a struct.
If Java had something like C#'s struct, we could do something like this in Java as well.
Is there any reason why Java could not
do this automatically i.e. switch to
BigInteger if an int was too small?
This is one of the advantage of dynamic typing, but Java is statically typed and prevents this.
In a dynamically type language when two Integer which are summed together would produce an overflow, the system is free to return, say, a Long. Because dynamically typed language rely on duck typing, it's fine. The same can not happen in a statically typed language; it would break the type system.
EDIT
Given that my answer and comment was not clear, here I try to provide more details why I think that static typing is the main issue:
1) the very fact that we speak of primitive type is a static typing issue; we wouldn't care in a dynamically type language.
2) with primitive types, the result of the overflow can not be converted to another type than an int because it would not be correct w.r.t static typing
int i = Integer.MAX_VALUE + 1; // -2147483648
3) with reference types, it's the same except that we have autoboxing. Still, the addition could not return, say, a BigInteger because it would not match the static type sytem (A BigInteger can not be casted to Integer).
Integer j = new Integer( Integer.MAX_VALUE ) + 1; // -2147483648
4) what could be done is to subclass, say, Number and implement at type UnboundedNumeric that optimizes the representation internally (representation independence).
UnboundedNum k = new UnboundedNum( Integer.MAX_VALUE ).add( 1 ); // 2147483648
Still, it's not really the answer to the original question.
5) with dynamic typing, something like
var d = new Integer( Integer.MAX_VALUE ) + 1; // 2147483648
would return a Long which is ok.
I'm not sure the question is clearly worded, but an example will be clearer.
I found out that will not work in Java:
int a = ...;
a = 5.0;
but this will:
int a = ...;
a += 5.0;
I.e., it seems that the = operator is type safe but += isn't.
Is there any deep reason for this or is it just another arbitrary decision language designers must take.
The reason is that math operations do some implicit casting:
a += 5.0; is evaluated as follows:
a = (int) ((double) a + 5.0);
Assignment, however, requires an explicit cast.
(It might be float rather than double, I don't remember which Java treats as decimal literals.)
To make life easier.
Let's go a little further. Consider:
byte b;
...
++b;
The increment is really doing:
b = (byte)(1 + (int)b);
Even using += it doesn't get any better:
b += b;
is:
b = (byte)((int)b+(int)b);
That would make these operators useless for byte/short/char.
Of course I wont be happy until we have arbitrary sized integers.