Possible loss of precision

S

Stefan Ram

When one writes

char x = 'C' + 1;
x = x + 1;

in a block, one gets an error message

»possible loss of precision«

for »x = x + 1«. Ok, fair enough: »x + 1« has type »int«,
and precision is lost when assigning this to the variable x
of type char.

I was somewhat surprised, though, that »char x = 'C' + 1;«
does not yield an error, even though the type of »'C' + 1«
is int, too. Possibly, this time, the compiler can figure
out that the actual value still fits into a char.

But then, »int i = 2.0;« gives an error, even though the
compiler should see that »2.0« can be represented as an int.
 
M

Michael Jung

char x = 'C' + 1;
x = x + 1;
in a block, one gets an error message
»possible loss of precision«
for »x = x + 1«. Ok, fair enough: »x + 1« has type »int«,
and precision is lost when assigning this to the variable x
of type char.
I was somewhat surprised, though, that »char x = 'C' + 1;«
does not yield an error, even though the type of »'C' + 1«
is int, too. Possibly, this time, the compiler can figure
out that the actual value still fits into a char.
But then, »int i = 2.0;« gives an error, even though the
compiler should see that »2.0« can be represented as an int.

You are right that x+1 is "promoted" to int and the second assignment
fails. In the other cases the compiler will follow JLS 15.28 => 5.2.
This concerns constant expressions first and the second allows a
narrowing in rare cases, of which the first line is one but the
"int i = 2.0" isn't.
 
G

Gene Wirchenko

You are right that x+1 is "promoted" to int and the second assignment
fails. In the other cases the compiler will follow JLS 15.28 => 5.2.
This concerns constant expressions first and the second allows a
narrowing in rare cases, of which the first line is one but the
"int i = 2.0" isn't.

Why isn't it though?

Sincerely,

Gene Wirchenko
 
E

Eric Sosman

Why isn't it though?

Speculation: The fact that a floating-point expression happens
to evaluate to a number with no fractional part doesn't mean the
value "is" an integer. One reasonable view of FP is that a value
is just a representative of a range of real values, namely, all
those real values that round to the representative. In this
view, converting 2.0 to 2 *does* lose precision, specifically,
the amount of wiggle room.

Then again, maybe the Java designers were just FP-wary.
 
J

Joerg Meier

Speculation: The fact that a floating-point expression happens
to evaluate to a number with no fractional part doesn't mean the
value "is" an integer. One reasonable view of FP is that a value
is just a representative of a range of real values, namely, all
those real values that round to the representative. In this
view, converting 2.0 to 2 *does* lose precision, specifically,
the amount of wiggle room.

My instinctive mental reply was that 2.0 instead of 2 is an explicit
instruction to use double, much like L or F, which you wouldn't want to be
silently ignored.

Liebe Gruesse,
Joerg
 
E

Eric Sosman

Speculation: The fact that a floating-point expression happens
to evaluate to a number with no fractional part doesn't mean the
value "is" an integer. One reasonable view of FP is that a value
is just a representative of a range of real values, namely, all
those real values that round to the representative. In this
view, converting 2.0 to 2 *does* lose precision, specifically,
the amount of wiggle room.

As a more concrete example,

static final double FOO = 17.316;
static final double BAR = 8.658;
int i = FOO / BAR; // Should javac be silent here?

Neither the numerator nor the denominator can be represented
exactly as stated, yet the quotient is (probably; cook up your
own numbers if mine are faulty) exactly 2.0. Would it be a
good idea to let this construct slide through unremarked? Or
should javac acknowledge the inherent imprecisions in the
operations leading to the exactly-integral result?
 
T

taqmcg

On 11/14/2013 6:31 PM, Eric Sosman wrote:

As a more concrete example,

static final double FOO = 17.316;
static final double BAR = 8.658;
int i = FOO / BAR; // Should javac be silent here?

Neither the numerator nor the denominator can be represented
exactly as stated, yet the quotient is (probably; cook up your
own numbers if mine are faulty) exactly 2.0. Would it be a
good idea to let this construct slide through unremarked? Or
should javac acknowledge the inherent imprecisions in the
operations leading to the exactly-integral result?

In this case it's pretty clear that the error should be signaled since we have a floating point expression that involves computations where roundoff'scould do weird things:
Consider the case when we use FOO = 3.3 and BAR = 1.1. Then the value of
(int) (FOO/BAR) == 2
and I certainly wouldn't want to have a language where

int i = 3.3/1.1;
gives a different result than
double x = 3.3;
double y = 1.1;
int i = x/y;

When the integer value of the ration is a power of 2, then the internal floating point representations will differ only in the exponent, so you're likely get 'correct' values when you do the computation, but for all other integer ratios the actual value that a proper Java program would computer will often differ from the 'correct' value. And even for powers of two one isgoing to run into problems if denormalized floats or doubles are being used.

If we exclude expressions there would be still hard cases:
E.g., if we were allowing
int i = 2.0;
presumably we'd also want to allow
int i = 1.234E3; // = 1234)
but then you might be surprised to get an error for the equivalent
int i = 1.2345E3; // = 1234.5
which doesn't really look very differnt.

And what would one do with:

int i = 123456789.f;

which seems like a normal integer, but since we've gone beyond the precision of floats we'd find that
float f = 123456789.f;
int i = (int) f;
yields
i == 123456792


We wouldn't run into this when converting doubles to ints, but a similar problem occurs for doubles to longs.

So any relaxation of the rule that there is a loss-of-precision error in converting floating point values to integral values would have to be either arbitrarily limited (say to |integers| < 100) or hedged with lots of caveats.. Having a simple rule that you need to signal such a conversion with a cast seems like a reasonable choice.

Personally I'd have preferred that Java be fully consistent here and not have the exception for operators like +=

double x = 1.;
int i = 0;
i += x; // is legal

but I believe that exception is essentially required by the (imho) folly ofautomatic promotion of bytes, shorts and chars to ints.

Regards,
Tom McGlynn
 
M

Michael Jung

Gene Wirchenko said:
(e-mail address removed)-berlin.de (Stefan Ram) writes: [...]
But then, »int i = 2.0;« gives an error, even though the
compiler should see that »2.0« can be represented as an int.
You are right that x+1 is "promoted" to int and the second assignment
fails. In the other cases the compiler will follow JLS 15.28 => 5.2.
This concerns constant expressions first and the second allows a
narrowing in rare cases, of which the first line is one but the
"int i = 2.0" isn't.
Why isn't it though?

Since compile-time constant expressions are FP-strict and could be done,
I guess it was seen as pathological and not worth the effort.

Michael
 
S

Stefan Ram

Patricia Shanahan said:
I think the difference is that there is a cast-free way of initializing
an int to the value 2.
On the other hand, without the special narrowing rule for initializers,
there would be no cast-free way of initializing a byte with the value 2.

It's the same in C++:

char c{ 7 }; // ok
char d{ 999 }; // usually an error
int i{ 3.0 }; // error

. Bjarne Stroustrup writes:

»The way C++11 avoids a lot of incompatibilities is by
relying on the actual values of initializers (such as 7
in the example above) when it can (and not just type)
when deciding what is a narrowing conversion. If a value
can be represented exactly as the target type, the
conversion is not narrowing.«

»Note that floating-point to integer conversions are
always considered narrowing -- even 7.0 to 7.«

http://www.stroustrup.com/C++11FAQ.html
 
M

Michael Jung

Patricia Shanahan said:
Gene Wirchenko said:
[...]
But then, »int i = 2.0;« gives an error, even though the
compiler should see that »2.0« can be represented as an int.
You are right that x+1 is "promoted" to int and the second assignment
fails. In the other cases the compiler will follow JLS 15.28 => 5.2.
This concerns constant expressions first and the second allows a
narrowing in rare cases, of which the first line is one but the
"int i = 2.0" isn't.
Why isn't it though?
Since compile-time constant expressions are FP-strict and could be done,
I guess it was seen as pathological and not worth the effort.
I think the difference is that there is a cast-free way of
initializing an int to the value 2.

Not if you wanted to do "int a = 2 * 0.5;" or something similar.
On the other hand, without the special narrowing rule for
initializers, there would be no cast-free way of initializing a byte
with the value 2.

They could have spent a suffix "B/b" (for non-hex integer literals) as
with "l/L".

Michael
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top