What does java compiler do in this case ?
for(int i=3;i< Math.sqrt(n);i=i+2)
Math.sqrt returns a double, so does Javac widen i to a double ?
If I want to use the int value back will i need to re-cast it ?
How does this actually work ?
Yes, i is widened to a temporary double for the comparison. The value of i itself is unaffected.
Math.sqrt returns a double, so does Javac widen i to a double?
Yes
If I want to use the int value back will i need to re-cast it?
Yes
How does this actually work ?
With primitives if you have a type that is wider than the other in the operation, the smaller is automatically converted for you.
What the language rules say is that i will be promoted to a double, and then compared with the return value of sqrt.
The compiler is free to do whatever it likes, as long as it results in the same behaviour.
I would be surprised if any compiler did anything other than promoted i to a double and did a comparison. The promotion is easy and cheap on most architectures. A correct alternative which was faster would be very hard to come up with.
There's no widening in Java, in the sense that is implied by the question: "the compiler widens i to a double". If you define a variable as int, it will always be an int. Internally, the representation of some types may be wider than necessary (for instance, a short may be stored as 4-bytes rather than 2), but this is not affected/determined by the way the variable is used.
Specifically, in the loop describes in the question the compiler emits code that converts the double returned from Math.sqrt() into an integer.
You can think of it as if the compiler rewrites the code as follows:
for(int i=3; (double) i < (Math.sqrt(n)); i=i+2)
(thanks to the people who commented. Fixed the snippet)
Related
I was practicing some castings in Java and I faced a situation for which I couldn't find any answers, anywhere. There are a lot of similar questions with answers, but none gave me an explanation for this particular case.
When I do something like
long l = 165787121844687L;
int i = (int) l;
System.out.println("long: " + l);
System.out.println("after casting to int: " + i);
The output is
long: 165787121844687
after casting to int: 1384219087
This result is very intriguing for me.
I know that the type long is a 64-bit integer, and the type int is a 32-bit integer. I also know that when we cast a larger type to a smaller one, we can lose information. And I know that there is a Math.toIntExact() method that is quite useful.
But what's the explanation for this "1384219087" output? There was loss of data, but why this number? How "165787121844687" became "1384219087"? Why does the code even compile?
That's it. Thanks!
165787121844687L in hex notation = 0000 96C8 5281 81CF
1384219087 in hex notation = 5281 81CF
So the cast truncated the top 32 bits as expected.
32-bits
deleted
▼▼▼▼ ▼▼▼▼
165_787_121_844_687L = 0000 96C8 5281 81CF ➣ 1_384_219_087
64-bit long ▲▲▲▲ ▲▲▲▲ 32-bit int
32-bits
remaining
If you convert these two numbers to hexadecimal, you get
96C8528181CF
528181CF
See what's happened here?
The Answer by OldProgrammer is correct, and should be accepted. Here is some additional info, and a workaround.
Java spec says so
Why does the code even compile?
When you cast a numeric primitive in Java, you take responsibility for the result including the risk of information loss.
Why? Because the Java spec says so. Always best to read the documentation. Programming by intuition is risky business.
See the Java Language Specification, section 5.1.3. Narrowing Primitive Conversion. To quote (emphasis mine):
A narrowing primitive conversion may lose information …
A narrowing conversion of a signed integer to an integral type T simply discards all but the n lowest order bits, where n is the number of bits used to represent type T. In addition to a possible loss of information about the magnitude of the numeric value, this may cause the sign of the resulting value to differ from the sign of the input value.
Math#…Exact…
When you want to be alerted to data loss during conversion from a long to a short, use the Math methods for exactitude. If the operation overflows, an execution is thrown. You can trap for that exception.
try
{
int i = Math.toIntExact( 165_787_121_844_687L ) ; // Convert from a `long` to an `int`.
}
catch ( ArithmeticException e )
{
// … handle conversion operation overflowing an `int` …
}
You will find similar Math#…Exact… methods for absolute value, addition, decrementing, incrementing, multiplying, negating, and subtraction.
I want to convert float value to int value or throw an exception if this conversion is not exact.
I've found the following suggestion: use Math.round to convert and then use == to check whether those values are equal. If they're equal, then conversion is exact, otherwise it is not.
But I've found an example which does not work. Here's code demonstrating this example:
String s = "2147483648";
float f = Float.parseFloat(s);
System.out.printf("f=%f\n", f);
int i = Math.round(f);
System.out.printf("i=%d\n", i);
System.out.printf("(f == i)=%s\n", (f == i));
It outputs:
f=2147483648.000000
i=2147483647
(f == i)=true
I understand that 2147483648 does not fit into integer range, but I'm surprised that == returns true for those values. Is there better way to compare float and int? I guess it's possible to convert both values to strings, but that would be extremely slow for such a primitive function.
floats are rather inexact concepts. They are also mostly pointless unless you're running on at this point rather old hardware, or interacting specifically with systems and/or protocols that work in floats or have 'use a float' hardcoded in their spec. Which may be true, but if it isn't, stop using floats and start using double - unless you have a fairly large float[] there is zero memory and performance difference, floats are just less accurate.
Your algorithm cannot fail when using int vs double - all ints are perfectly representable as double.
Let's first explain your code snippet
The underlying error here is the notion of 'silent casting' and how java took some intentional liberties there.
In computer systems in general, you can only compare like with like. It's easy to put in exact terms of bits and machine code what it means to determine whether a == b is true or false if a and b are of the exact same type. It is not at all clear when a and b are different things. Same thing applies to pretty much any operator; a + b, if both are e.g. an int, is a clear and easily understood operation. But if a is a char and b is, say, a double, that's not clear at all.
Hence, in java, all binary operators that involve different types are illegal. In basis, there is no bytecode to directly compare a float and a double, for example, or to add a string to an int.
However, there is syntax sugar: When you write a == b where a and b are different types, and java determines that one of two types is 'a subset' of the other, then java will simply silently convert the 'smaller' type to the 'larger' type, so that the operation can then succeed. For example:
int x = 5;
long y = 5;
System.out.println(x == y);
This works - because java realizes that converting an int to a long value is not ever going to fail, so it doesn't bother you with explicitly specifying that you intended the code to do this. In JLS terms, this is called a widening conversion. In contrast, any attempt to convert a 'larger' type to a 'smaller' type isn't legal, you have to explicitly cast:
long x = 5;
int y = x; // does not compile
int y = (int) x; // but this does.
The point is simply this: When you write the reverse of the above (int x = 5; long y = x;), the code is identical, it's just that compiler silently injects the (long) cast for you, on the basis that no loss will occur. The same thing happens here:
int x = 5;
long y = 10;
long z = x + y;
That compiles because javac adds some syntax sugar for you, specifically, that is compiled as if it says: long z = ((long) x) + y;. The 'type' of the expression x + y there is long.
Here's the key trick: Java considers converting an int to a float, as well as an int or long to a double - a widening conversion.
As in, javac will just assume it can do that safely without any loss and therefore will not enforce that the programmer explicitly acknowledges by manually adding the cast. However, int->float, as well as long->double are not actually entirely safe.
floats can represent every integral value between -2^23 to +2^23, and doubles can represent every integral value between -2^52 to +2^52 (source). But int can represent every integral value between -2^31 to +2^31-1, and longs -2^63 to +2^63-1. That means at the edges (very large negative/positive numbers), integral values exist that are representable in ints but not in floats, or longs but not in doubles (all ints are representable in double, fortunately; int -> double conversion is entirely safe). But java doesn't 'acknowledge' this, which means silent widening conversions can nevertheless toss out data (introduce rounding) silently.
That is what happens here: (f == i) is syntax sugared into (f == ((float) i)) and the conversion from int to float introduces the rounding.
The solution
Mostly, when using doubles and floats and nevertheless wishing for exact numbers, you've already messed up. These concepts fundamentally just aren't exact and this exactness cannot be sideloaded in by attempting to account for error bands, as the errors introduced due to the rounding behaviour of float and double cannot be tracked (not easily, at any rate). You should not be using float/double as a consequence. Either find an atomary unit and represent those in terms of int/long, or use BigDecimal. (example: To write bookkeeping software, do not store finance amounts as a double. do store them as 'cents' (or satoshis or yen or pennies or whatever the atomic unit is in that currency) in long, or, use BigDecimal if you really know what you are doing).
I want an answer anyway
If you're absolutely positive that using float (or even double) here is acceptable and you still want exactness, we have a few solutions.
Option 1 is to employ the power of BigDecimal:
new BigDecimal(someDouble).intValueExact()
This works, is 100% reliable (unless float to double conversion can knock a non-exact value into an exact one somehow, I don't think that can happen), and throws. It's also very slow.
An alternative is to employ our knowledge of how the IEEE floating point standard works.
A real simple answer is simply to run your algorithm as you wrote it, but to add an additional check: If the value your int gets is below -2^23 or above +2^23 then it probably isn't correct. However, there are still a smattering of numbers below -2^23 and +2^23 that are perfectly representable in both float and int, just, no longer every number at that point. If you want an algorithm that will accept those exact numbers as well, then it gets much more complicated. My advice is not to delve into that cesspool: If you have a process where you end up with a float that is anywhere near such extremes, and you want to turn them to int but only if that is possible without loss, you've arrived at a crazy question and you need to rewire the parts that you got you there instead!
If you really need that, instead of trying to numbercrunch the float, I suggest using the BigDecimal().intValueExact() trick if you truly must have this.
class Test
{
public static void main(String[] args)
{
short s=2,s1=200,s2;
s2=s+s1; // error: "possible loss of precision"
System.out.println(s2);
}
}
Why does assigning the result of adding two shorts to a short cause a compile error?
Because in order to perform arithmetic operations on shorts, the compiler will widen them to integers first:
S2 = s + s1
Is actually
S2 = (int)s +(int)s1
Where the right hand side has type int.
Becouse the sum of two shorts will be evaluated as an int, so you are assigning an int to a short.
You can solve this casting it back to short:
s2=(short)(s+s1);
Because when you assign a numeric literal, it defaults to Integer. The compiler doesnt inspect the value to check the precision will not be lost.
Additionally, Java will then perform integer arithmetic. See Primitive type 'short' - casting in Java for more information on short values.
In this case there may be an overflow, if 32,767 < s + s1 or s+s1 < -32,768
-32,768 - 32,767 is a range of possible values for a short variable.
This is due to Java using ints for arithmetic on small integers; after the addition, Java complains about the loss of precision due to casting back to a short.
It's worth noting several things:
The short data type is rarely useful outside of arrays.
If the sum involves the variable you're assigning to, you can avoid the need for an explicit cast by using the += operator, because the compound arithmetic/assignment operators all imply a cast to the type of the original variable (if it would be necessary).
I'm porting some code over from Processing to Java, and one issue I've come across is that processing's precompiler turns any doubles to floats. In Eclipse however, I've had to explicitly cast my values as float. Still though, I get errors that I don't understand. For instance, shouldn't putting an f at the end of this statement fix the type mismatch (Type mismatch: cannot convert from double to
float)?
springing[n] = .05*(.17*(n+1))f;
And even on simpler statements like this I get a type mismatch. What am I doing wrong?
float theta = .01f;
Well, your second statement is correct by Java's standards, but in your first example Java is probably trying to prevent you from converting doubles to floats due to a loss of precision that must be explicitly asked for by the programmer, as so:
double a = //some double;
float b = (float) a; //b will lose some of a's precision
According the Java grammar, the f suffix is only applicable to float literals. Your second statement should work. The first however is an expression and therefore requires a cast:
springing[n] = (float)(.05*(.17*(n+1)));
In your first example, the f suffix is only valid directly on literals, not after a whole expression. So write it in either of those ways (assuming springing is a float[]):
springing[n] = .05f*(.17f*(n+1));
springing[n] = (float)( .05*(.17*(n+1)));
The first does the whole calculation (apart from the n+1 part) in float, the second one calculates in double and then converts only the result to float.
(And in both cases, the parenthesis between .05 and .17 (and the matching one) is usually superfluous, since multiplication is associative. It might do some difference for really big values of n, but in these cases you would usually want the other way to avoid overflow.)
It appears that when you type in a number in Java, the compiler automatically reads it as an integer, which is why when you type in (long) 6000000000 (not in integer's range) it will complain that 6000000000 is not an integer. To correct this, I had to specify 6000000000L. I just learned about this specification.
Are there other number specifications like for short, byte, float, double? It seems like these would be good to have because (I assume) if you could specify the number you're typing in is a short then java wouldn't have to cast it - that is an assumption, correct me if I'm wrong. I would normally search this question myself, but I don't know what this kind of number specification is even called.
There are specific suffixes for long (e.g. 39832L), float (e.g. 2.4f) and double (e.g. -7.832d).
If there is no suffix, and it is an integral type (e.g. 5623), it is assumed to be an int. If it is not an integral type (e.g. 3.14159), it is assumed to be a double.
In all other cases (byte, short, char), you need the cast as there is no specific suffix.
The Java spec allows both upper and lower case suffixes, but the upper case version for longs is preferred, as the upper case L is less easy to confuse with a numeral 1 than the lower case l.
See the JLS section 3.10 for the gory details (see the definition of IntegerTypeSuffix).
By default any integral primitive data type (byte, short, int, long) will be treated as int type by java compiler. For byte and short, as long as value assigned to them is in their range, there is no problem and no suffix required. If value assigned to byte and short exceeds their range, explicit type casting is required.
Ex:
byte b = 130; // CE: range is exceeding.
to overcome this perform type casting.
byte b = (byte)130; //valid, but chances of losing data is there.
In case of long data type, it can accept the integer value without any hassle. Suppose we assign like
long l = 2147483647; //which is max value of int
in this case no suffix like L/l is required. By default value 2147483647 is considered by java compiler is int type. Internal type casting is done by compiler and int is auto promoted to Long type.
long l = 2147483648; //CE: value is treated as int but out of range
Here we need to put suffix as L to treat the literal 2147483648 as long type by java compiler.
so finally
long l = 2147483648L;// works fine.
I hope you won't mind a slight tangent, but thought you may be interested to know that besides F (for float), D (for double), and L (for long), a proposal has been made to add suffixes for byte and short—Y and S respectively. This would eliminate to the need to cast to bytes when using literal syntax for byte (or short) arrays. Quoting the example from the proposal:
MAJOR BENEFIT: Why is the platform
better if the proposal is adopted?
cruddy code like
byte[] stuff = { 0x00, 0x7F, (byte)0x80, (byte)0xFF};
can be recoded as
byte[] ufum7 = { 0x00y, 0x7Fy, 0x80y, 0xFFy };
Joe Darcy is overseeing Project Coin for Java 7, and his blog has been an easy way to track these proposals.
These are literals and are described in section 3.10 of the Java language spec.
It seems like these would be good to
have because (I assume) if you could
specify the number you're typing in is
a short then java wouldn't have to
cast it
Since the parsing of literals happens at compile time, this is absolutely irrelevant in regard to performance. The only reason having short and byte suffixes would be nice is that it lead to more compact code.
Java has two types of data type :
Primitive Data-Type
Non-Primitive Data-Type
Certain data types require specifications like long, float, and double.
While assigning any of the above data types to any variable always remember to....
End the value with a "d" in double data type.
End the value with a "L" in long data type.
End the value with a "f" in float data type.
Example:
long number = 15000000000L;
float mysecondnum = 5.75f;
double mynumber = 19.99d;
More info:
The size of the long data type is 8 bytes.
The size of the float data type is 4 bytes.
The size of the double data type is 8 bytes.
The precision level of the long data type is up to 7-8 decimal points while the float data type is up to 15 decimal points.
Edit:
Along with this typecasting can be used to change the primitive data type from one to another.
Widening Casting(automatically): smaller type to a larger type size
Narrowing Casting(manually): larger type to a smaller size type
To understand why it is necessary to distinguish between int and long literals, consider:
long l = -1 >>> 1;
versus
int a = -1;
long l = a >>> 1;
Now as you would rightly expect, both code fragments give the same value to variable l. Without being able to distinguish int and long literals, what is the interpretation of -1 >>> 1?
-1L >>> 1 // ?
or
(int)-1 >>> 1 // ?
So even if the number is in the common range, we need to specify type. If the default changed with magnitude of the literal, then there would be a weird change in the interpretations of expressions just from changing the digits.
This does not occur for byte, short and char because they are always promoted before performing arithmetic and bitwise operations. Arguably their should be integer type suffixes for use in, say, array initialisation expressions, but there isn't. float uses suffix f and double d. Other literals have unambiguous types, with there being a special type for null.