widening type conversions in Java - java

Herbert Schild states in Java A Beginner's Guide:
an automatic type conversion will take place if (a) the two types are compatible and (b) the destination type is larger then the source type
But: He then casts a long into a double so (b) is violated as a 64-bit integer is obviously bigger than a 32-bit type. This is a little confusing and counterintuitive at first.
Shouldn't the condition refined to
the destination type is larger or smaller then the source type given that such a conversion then takes only place if no data is lost as the destination type is sufficiently big enough to hold the data of the source type?

Both double and long are 64-bit. However, assigning a 64-bit integer to a double may cause precision loss, which is why an explicit cast is required. Therefore there's no automatic type conversion taking place in this case.

Actually double is also 64 bits in size.
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

Such a conversion is called widening conversion which can lead to loss of precision but is allowed.
See the accepted answer for a similar question
Why does Java implicitly (without cast) convert a `long` to a `float`?
http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html
A widening conversion of an int or a long value to float, or of a long
value to double, may result in loss of precision - that is, the result
may lose some of the least significant bits of the value. In this
case, the resulting floating-point value will be a correctly rounded
version of the integer value, using IEEE 754 round-to-nearest mode
long: The long data type is a 64-bit two's complement integer. The
signed long has a minimum value of -263 and a maximum value of 263-1.
In Java SE 8 and later, you can use the long data type to represent an
unsigned 64-bit long, which has a minimum value of 0 and a maximum
value of 264-1. Use this data type when you need a range of values
wider than those provided by int. The Long class also contains methods
like compareUnsigned, divideUnsigned etc to support arithmetic
operations for unsigned long.
double: The double data type is a double-precision 64-bit IEEE 754
floating point. Its range of values is beyond the scope of this
discussion, but is specified in the Floating-Point Types, Formats, and
Values section of the Java Language Specification. For decimal values,
this data type is generally the default choice. As mentioned above,
this data type should never be used for precise values, such as
currency.

Related

Is there an equivalent java constant for go's MaxUint64

go has the constant MaxUint32, for insigned integers, but does Java have an equivalent constant? Cuz I noticed that MaxUint32 is 4294967295 and Integer.MAX_VALUE is 2x that.
What would be the java equivalent of
r := float64(stringHash(source)) / (float64(math.MaxUint32) + 1)
What's the difference between a float in Java and a float64 in go?
According to this Question on Stack Overflow, MaxUint32 is a constant for the maximum 32-bit number available within in the Go type system, ranging from 0 to 4,294,967,295.
As for Java, I can explain that Java has only signed numeric types. As commented by tgdavies, that means the largest signed 32-bit integer in Java is half that of the unsigned 32-bit integer in Go: 2,147,483,647. As a workaround for compatibility with Go, just us a 64-bit long in Java to accommodate values coming in from Go.
In recent years a facility was added to address numbers as if unsigned. But I believe that should be used only on an exceptional basis. See Declaring an unsigned int in Java, especially this Answer.
And I can quote from The Java Tutorials, provided free-of-cost by Oracle corp, which you should read before posting here on basic Java matters:
The eight primitive data types supported by the Java programming language are:
byte: The byte data type is an 8-bit signed two's complement integer. It has a minimum value of -128 and a maximum value of 127 (inclusive). The byte data type can be useful for saving memory in large arrays, where the memory savings actually matters. They can also be used in place of int where their limits help to clarify your code; the fact that a variable's range is limited can serve as a form of documentation.
short: The short data type is a 16-bit signed two's complement integer. It has a minimum value of -32,768 and a maximum value of 32,767 (inclusive). As with byte, the same guidelines apply: you can use a short to save memory in large arrays, in situations where the memory savings actually matters.
int: By default, the int data type is a 32-bit signed two's complement integer, which has a minimum value of -231 and a maximum value of 231-1. In Java SE 8 and later, you can use the int data type to represent an unsigned 32-bit integer, which has a minimum value of 0 and a maximum value of 232-1. Use the Integer class to use int data type as an unsigned integer. See the section The Number Classes for more information. Static methods like compareUnsigned, divideUnsigned etc have been added to the Integer class to support the arithmetic operations for unsigned integers.
long: The long data type is a 64-bit two's complement integer. The signed long has a minimum value of -263 and a maximum value of 263-1. In Java SE 8 and later, you can use the long data type to represent an unsigned 64-bit long, which has a minimum value of 0 and a maximum value of 264-1. Use this data type when you need a range of values wider than those provided by int. The Long class also contains methods like compareUnsigned, divideUnsigned etc to support arithmetic operations for unsigned long.
float: The float data type is a single-precision 32-bit IEEE 754 floating point. Its range of values is beyond the scope of this discussion, but is specified in the Floating-Point Types, Formats, and Values section of the Java Language Specification. As with the recommendations for byte and short, use a float (instead of double) if you need to save memory in large arrays of floating point numbers. This data type should never be used for precise values, such as currency. For that, you will need to use the java.math.BigDecimal class instead. Numbers and Strings covers BigDecimal and other useful classes provided by the Java platform.
double: The double data type is a double-precision 64-bit IEEE 754 floating point. Its range of values is beyond the scope of this discussion, but is specified in the Floating-Point Types, Formats, and Values section of the Java Language Specification. For decimal values, this data type is generally the default choice. As mentioned above, this data type should never be used for precise values, such as currency.
boolean: The boolean data type has only two possible values: true and false. Use this data type for simple flags that track true/false conditions. This data type represents one bit of information, but its "size" isn't something that's precisely defined.
char: The char data type is a single 16-bit Unicode character. It has a minimum value of '\u0000' (or 0) and a maximum value of '\uffff' (or 65,535 inclusive).
That last one, char, is legacy, and should generally be avoided in favor of using code point integer numbers.
All of those primitive types have wrapper classes, when you need an object.
For extra large integers, use BigInteger class.
For extra large/small fractional numbers, use BigDecimal. Also use BigDecimal when you need accuracy rather than speed-of-execution. Uses cases include fractional money matters.

Type Conversion in Java Double to Int

I am trying to convert a double value of 2147483648 to integer, After typecasting it, I get output as 2147483647, the number is reduced by 1. I know that this is happening because of overflow, but is there a way where I can convert it to int type without loosing its precision?
As aforementioned in previous answers, you can use the long primitive data type, or the BigInteger reference type. Using integral data types rather than floating points would be better for representing integers exactly.
As a side note, since Java SE 8, one can use the Integer class to use int for unsigned arithmetic. Per the Oracle doc - "Use the Integer class to use int data type as an unsigned integer". Unsigned integers have a greater maximum value than int. This question can be of use: Declaring an unsigned int in Java.
This is my first answer, hope you solved your problem! :)
I can think of 2 options.
Long
Unlike int, which is a 32-bit signed integer data type, long is a 64-bit signed integer data type. This means that the largest value it can store is 9223372036854775807. The number that you are trying to store, 2147483648, is well inside that range.
BigInteger
This is a reference type that represents an "immutable arbitrary-precision integer". You can create a BigInteger instance that represents 2147483648 by doing
new BigInteger("2147483648")
Learn more about it here: https://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html
Max Values
The maximum integer, as indicated in the Integer class by the static final int MAX_VALUE, is 2^31-1 (2,147,483,647). This value is the maximum integer as it is the largest 32-bit signed integer.
The maximum double, as indicated in the Double class by the static final int MAX_VALUE, is (2-2^(-52))(2^1023). The double data type follows the double-precision 64-bit IEEE 754 floating point format to express a wide range of dynamic numerical values.
Narrowing Primitive Conversion
In you're observation, you have a double with a value 2,147,483,648 which you attempt to convert to an integer by type casting.
double d = 2147483648;
int i = (int) d;
Casting between primitive types allows your to convert the value of one primitive type to another. Converting from a double to an integer is known as a Narrowing Primitive Conversion, wherein you:
"may lose information about the overall magnitude of a numeric value and may also lose precision and range."
The narrowing conversion of a floating-point number to an int is as follows:
If the floating-point number is NaN, the result is an int of 0.
Otherwise, if the floating-point number is not an infinity, the floating-point number is rounded to an integer value V, rounding toward zero using IEEE 754 round-toward-zero mode. If this integer value can be represented as an int, then the result is V.
Otherwise, one of the following two cases must be true: a. The value must be too small and the result is the smallest representable value of type int or b. The value must be too large and the result is the largest representable value of type int.
With the integer value of the double value larger than the Integer.MAX_VALUE, in order to allow for representation as an integer the value of Integer.MAX_VALUE is used.
Avoid Loss
To avoid the loss of precision you will need to either cast to a primitive type or a numeric Object wherein the maximum value is greater than 2147483648 (and resolution allows for accuracy to be maintained).
The long primitive type has a maximum value of 2^63-1 (9.223372e+18), which would be a good choice if you want to use numbers within the numeric integer space. Note that while the Long.MAX_VALUE is very large, Double.MAX_VALUE is MUCH larger due to the floating-point format.
double d = 2147483648;
long i = (long) d;
Converting from a double to an int will lose precision for numbers greater than Integer.MAX_VALUE or less that Integer.MIN_VALUE. There is no way to represent numbers outside that range as an int. It is mathematically impossible.
Converting from a double to an long will also lose precision. This will occur for all integers outside of Long.MIN_VALUE through Long.MAX_VALUE. But a second problem is that double itself is not able to represent all integers in that range ... so there will be loss of precision before the conversion1.
Moral:
Don't use floating point numbers if you need to represent integers precisely. Use an integral type (byte, short, char, int or long) ... or BigInteger.
1 - A double is a 64 bit IEE floating point number, which has 52 bits of precision and a sign bit. By contrast, a long has 64 bits of precision (including sign).

Rules governing narrowing of double to int

Please note I am NOT looking for code to cast or narrow a double to int.
As per JLS - $ 5.1.3 Narrowing Primitive Conversion
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.
So, when I try to narrow a 260 (binary representation as 100000100) to a byte then result is 4 because the lowest 8 bits is 00000100 which is a decimal 4 OR a long value 4294967296L (binary representation 100000000000000000000000000000000) to a byte then result is 0.
Now, why I want to know the rule for narrowing rule from double to int, byte etc. is when I narrow a double value 4294967296.0 then result is 2147483647 but when I narrow a long 4294967296L value then result is 0.
I have understood the long narrowing to int, byte etc. (discards all but the n lowest order bits) but I want to know what is going under the hoods in case of double narrowing.
I have understood the long narrowing to int, byte etc. (discards all but the n lowest order bits) but I want to know what is going under the hoods in case of double narrowing.
... I want to understand the why and how part.
The JLS (JLS 5.1.3) specifies what the result is. A simplified version (for int) is:
a NaN becomes zero
an Inf becomes "max-int" or "min-int"
otherwise:
round towards zero to get a mathematical integer
if the rounded number is too big for an int, the result becomes "min-int" or "max-int"
"How" is implementation specific. For examples of how it could be implemented, look at the Hotspot source code (OpenJDK version) or get the JIT compiler to dump some native code for you to look at. (I imagine that the native code maps uses a single instruction to do the actual conversion .... but I haven't checked.)
"Why" is unknowable ... unless you can ask one of the original Java designers / spec authors. A plausible explanation is a combination of:
it is easy to understand
it is consistent with C / C++,
it can be implemented efficiently on common hardware platforms, and
it is better than (hypothetical) alternatives that the designers considered.
(For example, throwing an exception for NaN, Inf, out-of-range would be inconsistent with other primitive conversions, and could be more expensive to implement.)
Result is Integer.MAX_VALUE when converting a double to an integer, and the value exceeds the range of an integer. Integer.MAX_VALUE is 2^31 - 1.
When you start with the double value 4294967296.0, it is greater than the greatest long value which is 2147483647 so the following rule is applied (from the page you cited) : The value must be too large (a positive value of large magnitude or positive infinity), and the result of the first step is the largest representable value of type int or long and you get 0x7FFFFFF = 2147483647
But when you try to convert 4294967296L = 0x100000000, you start from an integral type, so the rule is : A narrowing conversion of a signed integer to an integral type T simply discards all but the n lowest order bits so if n is less than 32 (8 bytes) you just get a 0.

Why does Java implicitly (without cast) convert a `long` to a `float`?

Every time I think I understand about casting and conversions, I find another strange behavior.
long l = 123456789L;
float f = l;
System.out.println(f); // outputs 1.23456792E8
Given that a long has greater bit-depth than a float, I would expect that an explicit cast would be required in order for this to compile. And not surprisingly, we see that we have lost precision in the result.
Why is a cast not required here?
The same question could be asked of long to double - both conversions may lose information.
Section 5.1.2 of the Java Language Specification says:
Widening primitive conversions do not
lose information about the overall
magnitude of a numeric value. Indeed,
conversions widening from an integral
type to another integral type do not
lose any information at all; the
numeric value is preserved exactly.
Conversions widening from float to
double in strictfp expressions also
preserve the numeric value exactly;
however, such conversions that are not
strictfp may lose information about
the overall magnitude of the converted
value.
Conversion of an int or a long value
to float, or of a long value to
double, may result in loss of
precision-that is, the result may lose
some of the least significant bits of
the value. In this case, the resulting
floating-point value will be a
correctly rounded version of the
integer value, using IEEE 754
round-to-nearest mode (§4.2.4).
In other words even though you may lose information, you know that the value will still be in the overall range of the target type.
The choice could certainly have been made to require all implicit conversions to lose no information at all - so int and long to float would have been explicit and long to double would have been explicit. (int to double is okay; a double has enough precision to accurately represent all int values.)
In some cases that would have been useful - in some cases not. Language design is about compromise; you can't win 'em all. I'm not sure what decision I'd have made...
The Java Language Specification, Chapter 5: Conversion and Promotion addresses this issue:
5.1.2 Widening Primitive Conversion
The following 19 specific conversions
on primitive types are called the
widening primitive conversions:
byte to short, int, long, float, or double
short to int, long, float, or double
char to int, long, float, or double
int to long, float, or double
long to float or double
float to double
Widening primitive conversions do not lose information about the overall magnitude of a numeric value.
...
Conversion of an int or a long value to float, or of a long value to double, may result in loss of precision-that is, the result may lose some of the least significant bits of the value. In this case, the resulting floating-point value will be a correctly rounded version of the integer value
To put it another way, the JLS distinguishes between a loss of magnitude and a loss of precision.
int to byte for example is a (potential) loss of magnitude because you can't store 500 in a byte.
long to float is a potential loss of precision but not magnitude because the value range for floats is larger than that for longs.
So the rule is:
Loss of magnitude: explicit cast required;
Loss of precision: no cast required.
Subtle? Sure. But I hope that clears that up.
Though you're correct that a long uses more bits internally than a float, the java language works on a widening path:
byte -> short -> int -> long -> float -> double
To convert from left to right (a widening conversion), there is no cast necessary (which is why long to float is allowed). To convert right to left (a narrowing conversion) an explicit cast is necessary.
Somewhere I heard this. Float can store in exponential form as is we write it. '23500000000' is stored as '2.35e10' .So, float has space to occupy the range of values of long. Storing in exponential form is also the reason for precision loss.

What is the inclusive range of float and double in Java?

What is the inclusive range of float and double in Java?
Why are you not recommended to use float or double for anything where precision is critical?
Java's Primitive Data Types
boolean:
1-bit. May take on the values true and false only.
byte:
1 signed byte (two's complement). Covers values from -128 to 127.
short:
2 bytes, signed (two's complement), -32,768 to 32,767
int:
4 bytes, signed (two's complement). -2,147,483,648 to 2,147,483,647.
long:
8 bytes signed (two's complement). Ranges from -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807.
float:
4 bytes, IEEE 754. Covers a range from 1.40129846432481707e-45 to 3.40282346638528860e+38 (positive or negative).
double:
8 bytes IEEE 754. Covers a range from 4.94065645841246544e-324d to 1.79769313486231570e+308d (positive or negative).
char:
2 bytes, unsigned, Unicode, 0 to 65,535
Java's Double class has members containing the Min and Max value for the type.
2^-1074 <= x <= (2-2^-52)·2^1023 // where x is the double.
Check out the Min_VALUE and MAX_VALUE static final members of Double.
(some)People will suggest against using floating point types for things where accuracy and precision are critical because rounding errors can throw off calculations by measurable (small) amounts.
Binary floating-point numbers have interesting precision characteristics, since the value is stored as a binary integer raised to a binary power. When dealing with sub-integer values (that is, values between 0 and 1), negative powers of two "round off" very differently than negative powers of ten.
For example, the number 0.1 can be represented by 1 x 10-1, but there is no combination of base-2 exponent and mantissa that can precisely represent 0.1 -- the closest you get is 0.10000000000000001.
So if you have an application where you are working with values like 0.1 or 0.01 a great deal, but where small (less than 0.000000000000001%) errors cannot be tolerated, then binary floating-point numbers are not for you.
Conversely, if powers of ten are not "special" to your application (powers of ten are important in currency calculations, but not in, say, most applications of physics), then you are actually better off using binary floating-point, since it's usually at least an order of magnitude faster, and it is much more memory efficient.
The article from the Python documentation on floating point issues and limitations does an excellent job of explaining this issue in an easy to understand form. Wikipedia also has a good article on floating point that explains the math behind the representation.
From Primitives Data Types:
float: The float data type is a single-precision 32-bit IEEE 754
floating point. Its range of values is
beyond the scope of this discussion,
but is specified in section 4.2.3
of the Java Language Specification. As
with the recommendations for byte
and short, use a float (instead of
double) if you need to save memory
in large arrays of floating point
numbers. This data type should never
be used for precise values, such as
currency. For that, you will need to
use the java.math.BigDecimal
class instead. Numbers and
Strings covers BigDecimal and
other useful classes provided by the
Java platform.
double: The double data type is a double-precision 64-bit IEEE 754
floating point. Its range of values is
beyond the scope of this discussion,
but is specified in section 4.2.3
of the Java Language Specification.
For decimal values, this data type is
generally the default choice. As
mentioned above, this data type should
never be used for precise values, such
as currency.
For the range of values, see the section 4.2.3 Floating-Point Types, Formats, and Values of the JLS.
Of course you can use floats or doubles for "critical" things ... Many applications do nothing but crunch numbers using these datatypes.
You might have misunderstood some of the various caveats regarding floating-point numbers, such as the recommendation to never compare for exact equality, and so on.

Categories