Am I Crazy? V. simple math question.

M

MS

Hi,

I haven't programmed in Java for a while and just wrote a program to calc
the odds of winning the British national lottery. BUT it is giving me the
wrong result !!

Here's the relevant bit:

public void DoCalc()
{
float winOdds = 49 * 48 * 47 * 46 * 45 * 44;
System.out.println(winOdds);

float anyOrder = 6 * 5 * 4 * 3 * 2;
System.out.println(anyOrder);

float res = winOdds / anyOrder;

System.out.println(res);
}

It outputs:

1.47841293E9
720.0
2053351.2

But the result should be:

10,068,347,520
720
13,983,816

Don't believe me? Paste the following into your calc program:

(49 * 48 * 47 * 46 * 45 * 44) / (6 * 5 * 4 * 3 * 2) =

Or let google do it:
http://www.google.co.uk/search?hl=e...+*+5+*+4+*+3+*+2)+=+&btnG=Google+Search&meta=

Am I going crazy? Why doesn't my Java program work?

Please help.

MS
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

MS said:
I haven't programmed in Java for a while and just wrote a program to
calc the odds of winning the British national lottery. BUT it is giving
me the wrong result !!

Here's the relevant bit:

public void DoCalc()
{
> float winOdds = 49 * 48 * 47 * 46 * 45 * 44;

I am pretty sure you make an integer overflow here.

try:

float winOdds = 49.0 * 48.0 * 47.0 * 46.0 * 45.0 * 44.0;

System.out.println(winOdds);

float anyOrder = 6 * 5 * 4 * 3 * 2;
System.out.println(anyOrder);

float res = winOdds / anyOrder;

System.out.println(res);
}

It outputs:

1.47841293E9
720.0
2053351.2

But the result should be:

10,068,347,520
720
13,983,816

Arne
 
M

MS

Arne Vajhøj emailed this:
I am pretty sure you make an integer overflow here.

try:

float winOdds = 49.0 * 48.0 * 47.0 * 46.0 * 45.0 * 44.0;

Many thanks. What you suggested gave a 'possible loss of precision' error,
when I swapped the floats to doubles I got the correct result.

Strange oversight by the Java designers that Java doesn't assume the .0 at
the end of any real number.

Thanks again.
 
J

Joshua Cranmer

MS said:
Hi,

I haven't programmed in Java for a while and just wrote a program to
calc the odds of winning the British national lottery. BUT it is giving
me the wrong result !!

Here's the relevant bit:

public void DoCalc()
{
float winOdds = 49 * 48 * 47 * 46 * 45 * 44;
// snip
}

It outputs:

1.47841293E9
720.0
2053351.2

But the result should be:

10,068,347,520
720
13,983,816
No it shouldn't. float refers to single-precision floating point
scientific values (base two, of course), so ones-digit accuracy may not
possible when working with millions, but the answer would be expressed
(if properly calculated) as something with E7.
Don't believe me? Paste the following into your calc program:
[snip]
Am I going crazy? Why doesn't my Java program work?
What Java is doing when it calculates winOdds is it is multiplying the
numbers /as integers/ and then converting to a float. The number
obviously exceeds 2^31-1, so it is rolling over. There are several ways
to avoid this, the easiest being
float winOdds = 1.0f * 49 * 48 * 47 * 46 * 45 * 44;
or
float winOdds = 49.0f * 48 * 47 * 46 * 45 * 44;

The answer is, no, you aren't crazy, but you do need to learn more about
when conversions get applied.
 
M

MS

Joshua Cranmer emailed this:
MS said:
Hi,

I haven't programmed in Java for a while and just wrote a program to
calc the odds of winning the British national lottery. BUT it is
giving me the wrong result !!

Here's the relevant bit:

public void DoCalc()
{
float winOdds = 49 * 48 * 47 * 46 * 45 * 44;
// snip
}

It outputs:

1.47841293E9
720.0
2053351.2

But the result should be:

10,068,347,520
720
13,983,816
No it shouldn't. float refers to single-precision floating point
scientific values (base two, of course), so ones-digit accuracy may not
possible when working with millions, but the answer would be expressed
(if properly calculated) as something with E7.
Don't believe me? Paste the following into your calc program:
[snip]
Am I going crazy? Why doesn't my Java program work?
What Java is doing when it calculates winOdds is it is multiplying the
numbers /as integers/ and then converting to a float. The number
obviously exceeds 2^31-1, so it is rolling over. There are several ways
to avoid this, the easiest being
float winOdds = 1.0f * 49 * 48 * 47 * 46 * 45 * 44;
or
float winOdds = 49.0f * 48 * 47 * 46 * 45 * 44;

The answer is, no, you aren't crazy, but you do need to learn more about
when conversions get applied.

Many thanks for the explanation.

Cheers.
 
L

Lew

MS said:
Arne Vajhøj emailed this:

Many thanks. What you suggested gave a 'possible loss of precision'
error, when I swapped the floats to doubles I got the correct result.

Strange oversight by the Java designers that Java doesn't assume the .0
at the end of any real number.

Huh? What "real" numbers?

There was absolutely no oversight on the part of the Java designers here. The
compiler worked exactly as documented.

49, 48, 47, et al. are /integers/.
<http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.1>

not float or double literals
<http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.2>

You did /integer/ calculation.

Arne pointed out that that cause an /integer/ overflow.

After which you converted the incorrect /integer/ value to float. (Why not
double?)

How is the compiler suppose to "assume the [sic] .0" in an integer?
 
E

Eric Sosman

MS said:
Arne Vajhøj emailed this:

Many thanks. What you suggested gave a 'possible loss of precision'
error, when I swapped the floats to doubles I got the correct result.

Strange oversight by the Java designers that Java doesn't assume the .0
at the end of any real number.

Strange oversight by the Java programmers who can't
distinguish between `int' and `double' constants ...

To put it another way: The eventual "target" of an
expression does not influence the way the expression is
calculated. You wrote `float winOdds =' and apparently
expected the right-hand side to somehow magically understand
that it should be evaluated in `float' arithmetic. But the
original is an expression involving `int' values multiplied
together, so it is evaluated according to the rules of `int'.
After the evaluation, the result is converted to `float' for
the assignment -- but by then it's too late, the overflow has
already occurred and the damage has been done.

Perhaps the commonest situation where this misunderstanding
crops up is

double fahrenheit = 212;
double celsius = 5 / 9 * (fahrenheit - 32);

As a test of your newfound understanding of how expressions
are evaluated, can you say what's wrong with this formula?
For extra credit, can you explain why

double celsius = (fahrenheit - 32) * 5 / 9;

works as (presumably) intended?
 
M

MS

Strange oversight by the Java designers that Java doesn't assume the
.0 at the end of any real number.

Huh? What "real" numbers?
...snip...
How is the compiler suppose to "assume the [sic] .0" in an integer?

Good point! Oops. (Bows head in shame).
 
T

Tom Hawtin

MS said:
Arne Vajhøj emailed this:

Many thanks. What you suggested gave a 'possible loss of precision'
error, when I swapped the floats to doubles I got the correct result.

A more literal correction would be to use float literals:

float winOdds = 49.0f * 48.0f * 47.0f * 46.0f * 45.0f * 44.0f;

But doubles are almost always a better choice than floats (exception for
large memory use and GPU support).

Another way of doing it is:

float winOdds = 49L * 48L * 47L * 46L * 45L * 44L;
Strange oversight by the Java designers that Java doesn't assume the .0
at the end of any real number.

It's more shortsightedness in this area. Fixed size integers really
should be a thing of the past. Automatic conversion from integer to
floating point values is dangerous. Implicit overflows of compile-time
constants could have been forced to be explicit.

Tom Hawtin
 
M

MS

Eric Sosman emailed this:
Strange oversight by the Java programmers who can't
distinguish between `int' and `double' constants ...

To put it another way: The eventual "target" of an
expression does not influence the way the expression is
calculated. You wrote `float winOdds =' and apparently
expected the right-hand side to somehow magically understand
that it should be evaluated in `float' arithmetic. But the
original is an expression involving `int' values multiplied
together, so it is evaluated according to the rules of `int'.
After the evaluation, the result is converted to `float' for
the assignment -- but by then it's too late, the overflow has
already occurred and the damage has been done.

Perhaps the commonest situation where this misunderstanding
crops up is

double fahrenheit = 212;
double celsius = 5 / 9 * (fahrenheit - 32);

As a test of your newfound understanding of how expressions
are evaluated, can you say what's wrong with this formula?
For extra credit, can you explain why

double celsius = (fahrenheit - 32) * 5 / 9;

works as (presumably) intended?

Thanks for the explanation, and all I can say in my defense is I assumed
that since I was defining a float I assumed the compiler would assume the
'.0'. Obviously I was wrong for the reasons explained. But this would have
worked, at least I think:

double a = 49;
double b = 48;
double c = 47;
double d = 46;
double e = 45;
double f = 44;
double res = a * b * c * d * e * f;

which isn't all that different from this:

double res = 49 * 48 * 47 * 46 * 45 * 44;

To TRY to answer your questions.

In the first part of your question...

double fahrenheit = 212;
double celsius = 5 / 9 * (fahrenheit - 32);

5 / 9 isn't evaluated to a real number because 5 and 9 are integers.

The 2nd part of your question...

double celsius = (fahrenheit - 32) * 5 / 9;
>works as (presumably) intended?

The question says the 2nd formula works. fahrenheit is already a double so
presumably that's why it works, but I'm not sure of the reasons. Please
explain.

Thanks for your help. (I feel like I'm back at university).

Regards,

MS
 
M

MS

Thanks.

Tom Hawtin emailed this:
A more literal correction would be to use float literals:

float winOdds = 49.0f * 48.0f * 47.0f * 46.0f * 45.0f * 44.0f;

But doubles are almost always a better choice than floats (exception for
large memory use and GPU support).

Another way of doing it is:

float winOdds = 49L * 48L * 47L * 46L * 45L * 44L;


It's more shortsightedness in this area. Fixed size integers really
should be a thing of the past. Automatic conversion from integer to
floating point values is dangerous. Implicit overflows of compile-time
constants could have been forced to be explicit.

Tom Hawtin
 
M

Mark Space

MS said:
Arne Vajhøj emailed this:

Many thanks. What you suggested gave a 'possible loss of precision'
error, when I swapped the floats to doubles I got the correct result.

I think it's strange Java doesn't provide for any kind of overflow
error. Anyone know if it's possible to get Java to throw an overflow
for calculations like this?
 
E

Eric Sosman

MS said:
Eric Sosman emailed this:
MS wrote:
[...]
Thanks for the explanation, and all I can say in my defense is I assumed
that since I was defining a float I assumed the compiler would assume
the '.0'. Obviously I was wrong for the reasons explained. But this
would have worked, at least I think:

double a = 49;
double b = 48;
double c = 47;
double d = 46;
double e = 45;
double f = 44;
double res = a * b * c * d * e * f;

which isn't all that different from this:

double res = 49 * 48 * 47 * 46 * 45 * 44;

The difference is in when the conversion(s) from `int'
to `double' occur. In the multi-line form, each `int' number
gets converted to `double' and assigned to its variable, and
then all the `double' variables are multiplied in `double'
arithmetic. In the single-line form, all the `int' constants
are multiplied together in `int' arithmetic (overflowing), and
the result is then converted to `double'.
To TRY to answer your questions.

In the first part of your question...

double fahrenheit = 212;
double celsius = 5 / 9 * (fahrenheit - 32);

5 / 9 isn't evaluated to a real number because 5 and 9 are integers.

Right. In more detail, `5 / 9' is evaluated according to
the rules of `int' arithmetic, yielding the `int' value zero.
Inside the parentheses, `32' is converted from `int' to `double'
and subtracted from fahrenheit, yielding the `double' value
180.0. Then the zero from the division is converted from `int'
to `double' and multiplied with the 180.0, yielding 0.0.
The 2nd part of your question...

double celsius = (fahrenheit - 32) * 5 / 9;

The question says the 2nd formula works. fahrenheit is already a double
so presumably that's why it works, but I'm not sure of the reasons.
Please explain.

Once again, it's the order of operations. The thing in
parentheses is evaluated just as before, producing 180.0.
Then the `5' is converted from `int' to `double' and multiplied
by 180.0 to yield 900.0 -- note that in this version, the
multiplication occurs before, not after, the division. Finally,
the `9' is converted from `int' to `double' and the division
(done in `double' this time) produces 100.0.

Operation order is important! Consider what you get from
your camera when you point-and-shoot, versus what you get when
you shoot-and-point ...
 
K

Karl Uppiano

Mark Space said:
I think it's strange Java doesn't provide for any kind of overflow error.
Anyone know if it's possible to get Java to throw an overflow for
calculations like this?

That's tricky. For a some operations, integer "overflow" or wrap -- is the
desired effect, and follows the JLS rules for int. For floats and doubles,
the IEEE rules apply, and they are more like what you would expect for real
numbers.
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

Mark said:
I think it's strange Java doesn't provide for any kind of overflow
error. Anyone know if it's possible to get Java to throw an overflow
for calculations like this?

I do not think so.

It is not a trivial thing to do on many processors including x86.

Arne
 
M

MS

Eric Sosman emailed this:
MS said:
Eric Sosman emailed this:
MS wrote:
[...]
Thanks for the explanation, and all I can say in my defense is I
assumed that since I was defining a float I assumed the compiler would
assume the '.0'. Obviously I was wrong for the reasons explained. But
this would have worked, at least I think:

double a = 49;
double b = 48;
double c = 47;
double d = 46;
double e = 45;
double f = 44;
double res = a * b * c * d * e * f;

which isn't all that different from this:

double res = 49 * 48 * 47 * 46 * 45 * 44;

The difference is in when the conversion(s) from `int'
to `double' occur. In the multi-line form, each `int' number
gets converted to `double' and assigned to its variable, and
then all the `double' variables are multiplied in `double'
arithmetic. In the single-line form, all the `int' constants
are multiplied together in `int' arithmetic (overflowing), and
the result is then converted to `double'.

Yes, I already understood this, that's why I gave the example.

Right. In more detail, `5 / 9' is evaluated according to
the rules of `int' arithmetic, yielding the `int' value zero.
Inside the parentheses, `32' is converted from `int' to `double'
and subtracted from fahrenheit, yielding the `double' value
180.0. Then the zero from the division is converted from `int'
to `double' and multiplied with the 180.0, yielding 0.0.

Horray !!

Once again, it's the order of operations. The thing in
parentheses is evaluated just as before, producing 180.0.
Then the `5' is converted from `int' to `double' and multiplied
by 180.0 to yield 900.0 -- note that in this version, the
multiplication occurs before, not after, the division. Finally,
the `9' is converted from `int' to `double' and the division
(done in `double' this time) produces 100.0.

Well, I got that right too, even if I wasn't exactly sure why.

Many thanks for your help, and for taking the time to post a question to
make me think about it properly, and for the follow up reply, which has
allowed me to fully understand this kind of evaluation process.

As far as my Java programming is concerned, I should be careful when using
real numbers and integers together, that the integers are converted to
reals, either done explicitly or by their position in the evaluation
order. Can you cast in Java --as you can in C-- like this, which would
allow the first formula to work?

double celsius = (double) 5 / (double) 9 * (fahrenheit - (double) 32);

Many thanks again.
 
P

Patricia Shanahan

MS wrote:
....
As far as my Java programming is concerned, I should be careful when
using real numbers and integers together, that the integers are
converted to reals, either done explicitly or by their position in the
evaluation order. Can you cast in Java --as you can in C-- like this,
which would allow the first formula to work?

double celsius = (double) 5 / (double) 9 * (fahrenheit - (double) 32);

Indeed you can. I would normally only do it for integer variables.
(double)5 and 5.0 have the same value. Using the ".0" syntax has the
advantage of working for fractions, and for numbers that are too big for
int.

Patricia
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top