implicit cast or normal cast when dividing 2 ints? - java

Is there any difference in performance between doing an int division with a cast or implicit cast
eg.
int a = 7;
int b = 3;
float x = ((float)a)/b;
or
float y = (a+0f)/b;

As the comments point out, the two versions are different because of operator precedence rules.
float y = a + 0f / b;
is actually equivalent to
float y = a + (0f / b);
i.e.
float y = a;
You should have written
float y = (a + 0f) / b;
Provided that you get the operator precedence correct, there is no difference, semantically, or in terms of performance. (The JIT compiler is highly likely to optimize away the artificial addition of zero.)
However, there is an important difference. Your second form is an unusual idiom, and unusual idioms make it harder for other people to read your code. It is best to write your code the "normal" way ... if you (or your boss!!) anticipate that other people may need to read / maintain it.
(And if you make a habit of getting the operator precedence wrong, they may need to read your code sooner than you expect ... :-) )

Related

Floating-point equality unexpectedly working

We are often taught that floating-point numbers should not be compared for exact equality. However, the following function, which returns the Golden Ratio when passed any positive number, does in fact compare doubles for equality and to my surprise it seems to always work:
public static double f(double x) {
double y;
while ((y = 1 + 1 / x) != x)
x = (x + y) / 2;
return x;
}
#Test
void test() {
assertEquals((1 + sqrt(5)) / 2, f(1.0)); // Passes!
}
I thought that maybe it works for some input arguments but not others. But even if I use JQwik's property testing, it still works!
#Property
void test2(#ForAll #Positive double x) {
assertEquals((1 + sqrt(5)) / 2, f(x)); // Always passes!
}
Can anyone tell me why I never come across a situation where the two floating-point numbers are different by a very small amount?
You were just lucky, in general you don't get exact equality. Try this for example:
public static void main(String[] args) {
var s = 0.0;
for (int i = 0; i < 10; i++) {
s += 0.1;
}
System.out.println(s == 1.0);
}
In your concrete example one would have to do a careful analysis to prove that your iteration always converges to the floating point number closest to phi. If sqrt also returns the closest floating point number to the exact root we would get exact equality.
... and to my surprise it seems to always work:
Not always.
When I tried f(-1/1.6180339887498949), the x and y values oscillated between two floating point values that differed in the last few bits #Steve Summit. Thus an infinite loop.
x:-0.61803398874989490 y:-0.61803398874989468 // Decimal notation
x:-0x1.3c6ef372fe950p-1 y:-0x1.3c6ef372fe94ep-1 // Hex notation
x:-0.61803398874989479 y:-0.6180339887498949
x:-0x1.3c6ef372fe94fp-1 y:-0x1.3c6ef372fe950p-1
x:-0.61803398874989490 y:-0.61803398874989468
x:-0x1.3c6ef372fe950p-1 y:-0x1.3c6ef372fe94ep-1
f(some_starting_x) generally converges to render an x, such that 1 + 1 / x is x again and so meeting the stopping condition.
Better routines can prove that if x is reasonably close, the while loop will eventually get close to the desired answer, yet even then, an oscillation, as shown above is possible. Thus using an iteration limit or close enough test is needed. Usually the 2 oscillation values, when close, they are massaged (e.g. averaged) to form the best answer. If not close, the looping simply failed to find a stable answer.
Can anyone tell me why I never come across a situation where the two floating-point numbers are different by a very small amount?
Inadequate testing.
Morale of the story:
Do not rely on only floating point equality, except in select cases.
f() was not a select case and deserved additional stopping code.
Ref: Two x with math property: x = 1 + 1/x:
x1 = 1.6180339887498948482045868343656...
x2 = -0.61803398874989484820458683436564...
Note x1*x2 == -1. x1 is the Golden_ratio φ.

What is the priority of casting in java?

if I have a line of code that goes something like
int s = (double) t/2
Is it the same as
int s = (double) (t/2)
or
int s = ((double) t)/2
?
See this table on operator precedence to make things clearer. Simply put, a cast takes precedence over a division operation, so it would give the same output as
int s = ((double)t) / 2;
As knoight pointed out, this is not technically the same operation as it would be without the parentheses, since they have a priority as well. However, for the purposes of this example, it will offer the same result, and is for all intents and purposes equivalent.

Why would i+=l compile, where i is int and l is long?

I spotted Java's +=, -=, *=, /= compound assignment operators (good question :)), but it had a part that I don't quite understand. Borrowing from that question:
int i = 5;
long l = 8;
Then i = i + l; will not compile but i += l; will compile fine.
The accepted answer to the linked question states that:
A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.
which gives that i += l; is the same as i = (int)((i) + (l)); with the exception that i is only evaluated once.
A long may be (IIRC even is guaranteed to be) longer than an int, and thus can hold a much greater range of values.
Given that this situation can very easily cause data loss due to necessary narrowing conversion at some point during execution of the statement (either r-value expression evaluation, or assignment), why is i += l; not a compile-time error or at least warning?
Basically, because i += l is compiled as if it were written i = (int) (i + l). There are similar "surprises" when adding int values to byte and char variables -- the assignment operator works while the plain addition operator does not.
Given that this situation can very easily cause data loss due to necessary narrowing conversion at some point during execution of the statement (either r-value expression evaluation, or assignment), why is i += l; not a compile-time error or at least warning?
It probably should be, as you said, either a compile-time error or at least warning. Most books and tutorials that I'm aware of introduce x += y; as shorthand for x = x + y;. I honestly don't know of any that make the distinction called out in section 15.26.2 Compound Assignment Operators of the JLS except one.
In chapter 2 (puzzle 9) of Java Puzzlers: Traps, Pitfalls, and Corner Cases the authors (Joshua Bloch & Neal Gafter) ask you to provide declarations for x and i such that this is a legal statement:
x += i;
and this is not:
x = x + i;
There are lots of solutions, including the first two lines of code that you posted in your question. The authors warn against using compound assignment operators on variables of types byte, short, and char, and recommend that when using these operators on variables of type int you should make sure that the RHS expression is not a long, float, or double.
They conclude with the following observation (emphasis mine):
In summary, compound assignment operators silently generate a cast. If the type of the result of the computation is wider than that of the variable, the generated cast is a dangerous narrowing cast. Such casts can silently discard precision or magnitude. For language designers, it is probably a mistake for compound assignment operators to generate invisible casts; compound assignments where the variable has a narrower type than the result of the computation should probably be illegal.
the reason for this is so you can do:
byte b = 1;
b += 1;
if += was expanded to b = b + 1 then the expression would not type check because the expression "b + 1" is of type int. all integral expressions in java are at least the int type even if you add two bytes together.
public class Test {
public static void main(String[] args) {
byte b = 1;
byte c = 2;
byte d = b + c;
}
}
Test.java:5: possible loss of precision
found : int
required: byte
byte d = b + c;
^
1 error

How to add two java.lang.Numbers?

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)

Why aren't op-assign operators type safe in java?

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.

Categories