• Post Reply Bookmark Topic Watch Topic
  • New Topic

primitive long and double compatibility  RSS feed

 
Ranch Hand
Posts: 71
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi ranchers!
I cannot fully understand the in/compatibility between long and double;
The following code points out that long can be converted into double, but it's illegal the contrary.

What puzzles me is that both primitives are ranged as 64 bit number.
So how is that possible?
 
Ranch Foreman
Posts: 1023
30
Android Chrome IntelliJ IDE Java MySQL Database
  • X Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Please remember that a double can handle factions/decimal numbers for instance 3.141592654 as well as whole numbers.
Where as long can not handle factions/decimal numbers, but only whole numbers.

Given your example code x is a whole number of 1000.
It then automatically gets converted to a double when you declare y.
Now y is 1000.00, or something close to that because it's a double.
1000.00 cannot be converted to int without the loosing the decimal places. In doing so you may lose some precision.

This is somewhat related to this posting/blog entry
Does 1.0 equal 0.9999999999999999...?
Which attempts to explain some of the concerns with dealing to floating point numbers in regards to comparisons.
 
Marshal
Posts: 56851
174
  • X Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You are not using the number as a long. 1_000 is an int literal, which requires conversion to a long before you can use it. The long form of the number is 1_000L.

What PL has said is the explanation for a basic language requirement: a double is regarded as a “wider” datatype that a long, so you can implicitly widen one way, but you cannot convert the other way without a cast. As PL has hinted, the cast will lose any fractional part of the number.
You can also get loss of precision the other way; it is possible to fit 1234567891011121314L into a long, but there is no guarantee that it can be exactly represented as a double.

1000 is one of the numbers that can be represented exactly as a double.
 
Daniele Barell
Ranch Hand
Posts: 71
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Pete Letkeman wrote:Please remember that a double can handle factions/decimal numbers for instance 3.141592654 as well as whole numbers.
Where as long can not handle factions/decimal numbers, but only whole numbers.

Given your example code x is a whole number of 1000.
It then automatically gets converted to a double when you declare y.
Now y is 1000.00, or something close to that because it's a double.
1000.00 cannot be converted to int without the loosing the decimal places. In doing so you may lose some precision.
...



Hi Pete,
thanks for the answer.
So double cannot get back to long because of the floating point, I mean the compiler thinks:" if there was decimal digits in double then the program would loose them. Let's advise of the possible lossy conversion!".
 
Pete Letkeman
Ranch Foreman
Posts: 1023
30
Android Chrome IntelliJ IDE Java MySQL Database
  • X Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You are welcome Daniele and yes it appears as though you have it correct now.
 
Sheriff
Posts: 5072
357
BSD
  • X Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Daniele Barell wrote:So double cannot get back to long because of the floating point


It cannot go back without losing precision, obviously. In general it can, you just need explicitly cast it to long.
 
Daniele Barell
Ranch Hand
Posts: 71
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell, I may loose some point of your discourse:

Campbell Ritchie wrote:You are not using the number as a long. 1_000 is an int literal, which requires conversion to a long before you can use it. The long form of the number is 1_000L.


I see.
But it's not a requirement. I mean I can use x without convert the literal to long:



Campbell Ritchie wrote:What PL has said is the explanation for a basic language requirement: a double is regarded as a “wider” datatype that a long, so you can implicitly widen one way, but you cannot convert the other way without a cast. As PL has hinted, the cast will lose any fractional part of the number.


OK

Campbell Ritchie wrote:You can also get loss of precision the other way; it is possible to fit 1234567891011121314L into a long, but there is no guarantee that it can be exactly represented as a double.


So why the compiler enables me to convert a long into double? 



by the way, you are right:

I'm not a math wiz...What's the meaning of the last 'E18'?



 
Daniele Barell
Ranch Hand
Posts: 71
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Liutauras Vilda wrote:

Daniele Barell wrote:So double cannot get back to long because of the floating point


It cannot go back without losing precision, obviously. In general it can, you just need explicitly cast it to long.



Hi Liutaras,
thanks for the clarification!
 
Bartender
Posts: 18883
77
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Grammar lesson:

"long" is an adjective - describing the implied noun (integer)

"double" is also an adjective - describing the implied noun float (floating-point number, to be precise)

In computer grammar terms, the "adjectives" are called modifiers.

So you have 2 different base types and therefore not only does precision vary, but so does the domain of the representable number set.

I'd have to check, but I think that Java is one of the languages that also supports the construct "long double". The implied noun here is float, since upconversion makes floats of integers, so "double" is the determining word.
 
Campbell Ritchie
Marshal
Posts: 56851
174
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Daniele Barell wrote:. . . I mean I can use x without convert the literal to long:

That code has an implicit cast it. You have an int expression to the right of the = sign and the JVM will interpret that as
long x = (long)(1_000);
Since the expression on the right is a constant expression, what many people call a compile‑time constant, the type conversion may be done by the compiler. If it is, it can be seen in the bytecode. You can view the bytecode with javap -c LongDouble. If you look in the Java® Language Specification (=JLS), you will find that longdouble is one of the 19 “widening primitive conversions”. These are specifically permitted by the language specification under all circumstances. If you scroll down, you will find that

A widening primitive conversion from int to float, or from long to float, or from long to double, may result in loss of precision - that is, the result may lose some of the least significant bits of the value.

If you scroll down a bit more, you will find there are also 22 “narrowing primitive conversions”. You will see from the example that they all require casts; if you attempt any of those conversions without a cast, the compiler must fail to accept the code, but the compiler error message about “possible loss of precision” isn't very clear.

. . . So why the compiler enables me to convert a long into double?  . . .

As it said earlier, “widening” conversions are always permitted.

If you go back to the JLS link I gave above, you will see there are comments about range, saying that casting doublefloat may cause small values to degenerate to 0f and large values to degenerate to ∞. That suggests that a number datatype is regarded as “wider” if its range of permissible values is greater, meaning there are more values between the largest and the smallest. A double and a long each occupy 64 bits, and the long can exactly define every whole number in its range. But the double has a much larger range, so it must miss out some values. I learnt about that first from the Deitel and Deitel Java5 book. There are ten long values between 1234567891011121310L and 1234567891011121320L(exc), but there are not ten different double values in that range, so the nearest double has to be chosen. In fact the output you obtained suggests to me that there are no double values in that range, which is why it ends 141 not 13x.

. . . What's the meaning of the last 'E18'?

Because you didn't specify any formatting, the output defaults to printing the fewest numbers necessary to identify the nearest distinct value. That turns out to be 1,234,567,890,101,114,100 but the default format is so‑called scientific format which on paper looks like 1.23456789101112141×10¹⁸, but older computers didn't have the pretty superscripts, so they write E18 instead of ×10¹⁸. Read that as meaning you move the decimal point 18 places to the right; in this case you will have to add a few 0s on the right.
If you are supposed to move the decimal point to the left, there will be a − after the E.

I am now going into hiding for embarrassment about how many posts I have

[edit]Additonal: I see that the 121314 at the right end of the number has turned into 14100 so the precision is worse than I thought. It also might mean that I have missed out a 0 at the right of the number I was discussing.
 
Tim Holloway
Bartender
Posts: 18883
77
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm not 100% certain about everything I'm about to assert because I'm going from IBM's floating-point specs and they are slightly different from the IEEE (Java ones). But here goes:

An integer is self-contained and it is at its heart a unitary value. A floating-point number is not. Floats have an exponent part and a mantissa part. The exponent scales the mantissa value. The mantissa is considered as a binary fraction - that is, 0.1 would have a value of 1/2. Given that, an exponent of 1 (meaning 2**1) and a mantissa of 0.100000000... would have an integral value of 1.

The difference between float, double and long double is primarily in the available amount of bit positions. The total bit occupancy of a float is 32 bits, a double is 64 bits and a long double is 128 bits.

Although, if memory serves, the IEEE spec does allow some extra bits for exponent values as well (IBM's S/360 version always had 8 bits for exponent and everything else was mantissa).

For a given number of exponent bits, adding bits to the overall value length does not increase the range of the number, but rather its precision. (also known as epsilon.). So downscaling from a double to a float, for example, would be the equivalent of chopping the value 9.3333333333 down to 9.333333. The approximate values are the same, but obviously not their exact values. Since the mantissa is a binary fraction and not a decimal one, a lot of decimal fractions are crude to begin with. 0.5 and 0.25 are easy (precise), but 0.1 is not (irrational, if I've got my terminology correct).

In the event that your floating-point system allows different-sized exponent bit allocations, downscaling where the target exponent cannot hold the original exponent value can potentially blow the number out of range. But in general, that should not result in infinity, but in a NaN value, since it's not literally infinity. And, of course raise a range exception.

So you have 2 vulnerabilities. First, that you will lose precision - downwards for positive numbers, upwards for negative numbers, and the loss of precision is not related to the related (nearest) integer value.

Secondly, that you will exceed the range of the integral reach of the number (exponent size).

Loss of precision is annoying, but non-fatal. Unless you accumulate enough erosion to, say, destroy an interplanetary exploration vehicle or steal from someone's paycheck. Loss of exponent range, on the other hand is an immediate problem, since it puts an immediate end to all further computation.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!