Tips and Tidbits from someone Sun felt was Certifiable

Today | ???common.rss??? | ???common.rdf??? | ???common.atom??? | ???common.other???
 

Most folks know the difference between a widening conversion and a narrowing conversion, but I find that quite a few SCJP candidates miss out on one of the intricacies of these things. I think the problem is the way in which these conversions are normally explained.

In most cases, I see narrowing and widening conversions explained based on the size (in bytes) of the types involves. Converting a byte, which is 8 bits, to an int, which is 32 bits, is considered a widening conversion because we can easily fit all 8 bits of the byte into the int. Converting a short, which is 16 bits, to a byte, which is 8 bits, is considered a narrowing conversion. That's easy to see because we can't possibly fit all 16 bits of the short into the byte. If those extra 8 bits contain data, we're going to lose it. In fact, this view seems to make perfect sense because you can think of the memory space widening or narrowing for the data. An int is literally "wider" than a byte.

As you can see, this explanation makes sense and seems very logical. And, in most cases, it works. Notice that I said, "in most cases."

The order of numeric data types (from narrowest to widest), looks like this:

byte -> short/char -> int -> long -> float -> double

The previous explanation breaks down in two cases. Let me rewrite that progression but I'll replace the data types with their respective sizes, in bytes:

8 -> 16/16 -> 32 -> 64 -> 32 -> 64

The first thing you probably notice is that we increase in size from left to right, except for one point at which we decrease in size from 64 bits to 32 bits. What is that, you ask? Well, that's the conversion from a long, which is 64 bits, to a float, which is only 32 bits. Converting a long to a float is a widening conversion.

Obviously, using the previous explanation, this makes no sense. How could you fit 64 bits of data into 32 bits? Well, you can't. However, you can represent the same data that is represented in 64 bits in 32 bits if you look at the bits in a different way. If you're familiar with scientific notation, you know that 10,000 is equivalent to 1x104. Note that we just wrote the same number two different ways. Floating point numbers are the scientific notation of the computer world. ;) With a float, only a small number of the bits represent the number while another bunch of bits represents the exponent to which this number should be raised - it's effectively base 2 scientific notation.

So what does that mean as far as our conversions go? Well, using scientific notation, we can represent a far wider range of values using fewer bits than if we had not used scientific notation. The largest number a long can hold is 263 - 1. Meanwhile, a float, which is 32 bits shorter than a long, can hold up to (2-2-23)·2127.

You see, any value that can be represented by a long can be approximated by a float. Therefore, if you convert a long to a float, you have no risk of losing data. That means that a conversion from a long to a float, even though a long is "bigger" than a float, is considered a widening conversion. You see, whether a conversion is considered to be widening or narrowing is really about the "range" of the types, not the size. It just so happens that, if the types represent numbers in the same way (as ints and longs do), the comparison is the same - a larger size means a larger range. It's when the data types don't represent numbers in the same way that we have a problem, as is the case with longs and floats.

Note: There are cases in which a number can not be exactly represented by a float, but it can be approximated. I might go into this in greater depth in a future post. If you're interested, this page contains a very detailed explanation of floating point numbers in Java.

Okay, so now perhaps you're wondering where the other problem is in the chain I showed earlier. After all, I did say that there were two problems with it. Well, the other problem occurs with shorts and chars.

Both a short and a char is represented by 16 bits of data. Therefore, you can easily fit whatever is in one into the other. However, casting from one to the other is always considered to be a narrowing conversion? Why?

Once again, we have a case in which the two data types represent data in different ways. A short is a "signed" data type, meaning that it can represent both poitive and negative values. It contains 15 bits of numerical data and 1 sign bit to denote the sign. A char, on the other hand, is an "unsigned" data type; it holds only positive values. Therefore, a char has no need for a sign bit and contains 16 bits of numerical data. The range for a short is from -32768 to 32767. The range for a char is 0 to 65535.

As you can see, there are values that are representable by a short that can't be represented by a char; the value -12 is a good example. Likewise, there are values that are representable by a char that are not representable by a short; the value 64000 is a good example. Therefore, when we convert from a short to a char or vice versa, we run the risk of losing data. Therefore, converting in either direction is considered a narrowing conversion. In fact, converting a byte, which is only 8 bits, to a char is considered a narrowing conversion as well because a byte is signed. Char, as the only "unsigned" primtive data type is Java, is a little special.

Well, I think that's about enough for my discussion of narrowing and widening conversions. Just remember that these conversions are all about range, not about size.

Until next time,
Corey


I think This article was simpally wonderful
Very helpful, consise and to the point. Thanks
I think this article is wonderful and the explanation is precise and very well descriptive.
Article is really very simple and precise and this is the best artice that i have ever read on type coversions. thanks to whomsoever concerned. bhaskara.navuluri
Good topic. However, one doubt... Isn't double having the same range as that of long ? If so, then if conversion from float to long is "narrowing" then shouldn't the conversion from float to double also be "narrowing" ?
Isn't double having the same range as that of long ? If so, then if conversion from float to long is "narrowing" then shouldn't the conversion from float to double also be "narrowing" ?

Don't get confused between variable "size" and variable "range". Doubles and longs each take up 64 bits of space. However, the range of a double is far greater than that of a long. Because of this, not every value that can be represented by a double can be represented by a long. Take, for example, 2^70. This number can be represented by a double, but not by a long. Therefore, when you convert from a double to a long, it is considered a narrowing conversion. When you talk about whether or not something is a narrowing or widening conversion, you need to look at the ranges of the variables in question, not their sizes.

Going the other way, you need to worry about losing precision. Longs exactly represent values, while doubles are generally approximations due to the fact that they use a form of scientific notation to represent any given value.

Thats right. I think he should rework that explanation. I tried but can't come up with the right words... Converting from long to float you can loose precision. Converting from float to long you can lose scale.
wow - thanks for the explanation - so much clearer now! I was really confused with, when overloading a method, why a variable of type long would go into a float rather than double.
wht is implicit narrowing conversion? byte b = 32; legal int a =32; byte b = a; illegal why?

When you place a integer literal in your code, such as '32', the compiler interprets this as an int. Normally, you can't assign an int to a byte, but, in the case of an integer literal, the compiler tries to take an extra step to help you out. In this case, it takes a look at the literal (32) and asks itself, "Will that fit in a byte?" The answer, of course, is "Yes", so the compiler allows the line. Try changing that to something like this:

byte b = 3000;

You'll see that this no longer works.

However, when you're trying to assign a variable to a byte, the compiler automatically disallows it. Why? Because the compiler doesn't know what the value of that variable will be at runtime. Will it be 32? Will it be 3000? The compiler doesn't know, so it complains. A variable isn't like a literal - the compiler can't guess what it will be, so it has to err on the side of caution and throw an error.

But! We can easily see that we're just setting the value of 32 into that variable, right? Well, yes, we can. However, the compiler simply isn't that smart. It doesn't look at the logic of your code to figure out what you're doing. And that's why you get a compiler error on that line.

I hope that helps.

is there any sharp demarkation on wht kind of exception are thrown by the JVM and wht kind of exceptions should be thrown by the programmer!!Is there any clear demarkation? Suppose we know tht one kind of exception is typically thrown by JVM.What happens if programmer specifies an exception tht is known to be typically thrown by JVM by an exclusive throws modifier relating to his code.I mean to say in this case the exception will be thrown by whom? by JVM or by programmer's code!!! Is there any rule tht runtime exceptions are thrown by JVM or I/O exceptions are thrown by JVM. I would like to thank u for the answer to my previous question relating to casting....
Please explain about the followings this super this() super() please clarify about the word *current object* please give some examples

Nihar,

If you have questions or comments about this specific article, feel free to post here. However, your questions do not relate to this article, so I feel they're better posted elsewhere. If you would, please take your questions to a more suitable location, such as The Javaranch SCJP forum or The Javaranch Java in General forum. Not only will your questions be more closely related to the topic material, but you'll get more people than just me looking at them to respond.

TrackBack to http://radio.javaranch.com/corey/addTrackBack.action?entry=1086120368000