# Am I Crazy? V. simple math question.

Discussion in 'Java' started by MS, Apr 8, 2007.

1. ### MSGuest

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) =

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

MS

MS, Apr 8, 2007

2. ### =?ISO-8859-1?Q?Arne_Vajh=F8j?=Guest

MS wrote:
> 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

=?ISO-8859-1?Q?Arne_Vajh=F8j?=, Apr 8, 2007

3. ### Tom HawtinGuest

MS wrote:
> float winOdds = 49 * 48 * 47 * 46 * 45 * 44;

> 1.47841293E9
> 720.0
> 2053351.2
>
> But the result should be:
>
> 10,068,347,520
> 720
> 13,983,816

What's the value of Integer.MAX_VALUE?

Tom Hawtin

Tom Hawtin, Apr 8, 2007
4. ### MSGuest

Arne Vajhøj emailed this:
> MS wrote:
>> 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;

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.

MS, Apr 8, 2007
5. ### Joshua CranmerGuest

MS wrote:
> 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;

when conversions get applied.

Joshua Cranmer, Apr 8, 2007
6. ### MSGuest

> What's the value of Integer.MAX_VALUE?

Too low for my needs.
Thanks.

MS, Apr 8, 2007
7. ### MSGuest

Joshua Cranmer emailed this:
> MS wrote:
>> 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;
>
> when conversions get applied.

Many thanks for the explanation.

Cheers.

MS, Apr 8, 2007
8. ### LewGuest

MS wrote:
> Arne VajhÃ¸j emailed this:
>> MS wrote:
>>> 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;

>
> 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?

--
Lew

Lew, Apr 8, 2007
9. ### Eric SosmanGuest

MS wrote:
> Arne Vajhøj emailed this:
>> MS wrote:
>>> 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;

>
> 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?

--
Eric Sosman
lid

Eric Sosman, Apr 8, 2007
10. ### LewGuest

Tom Hawtin wrote:
>> What's the value of Integer.MAX_VALUE?

MS wrote:
> Too low for my needs.

Exactly! That was the problem!

--
Lew

Lew, Apr 8, 2007
11. ### MSGuest

>> 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).

MS, Apr 8, 2007
12. ### Tom HawtinGuest

MS wrote:
> Arne Vajhøj emailed this:
>> MS wrote:

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

>>
>> I am pretty sure you make an integer overflow here.

>> 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.

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

Tom Hawtin, Apr 8, 2007
13. ### MSGuest

Eric Sosman emailed this:
> MS wrote:
>> Arne Vajhøj emailed this:
>>> MS wrote:
>>>> 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;

>>
>> 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?

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;

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

MS, Apr 8, 2007
14. ### MSGuest

Thanks.

Tom Hawtin emailed this:
> MS wrote:
>> Arne Vajhøj emailed this:
>>> MS wrote:

>
>>> > float winOdds = 49 * 48 * 47 * 46 * 45 * 44;
>>>
>>> I am pretty sure you make an integer overflow here.

>
>>> 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.

>
> 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
>
>
>

MS, Apr 8, 2007
15. ### Mark SpaceGuest

MS wrote:
> 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.
>

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?

Mark Space, Apr 8, 2007
16. ### Eric SosmanGuest

MS wrote:
> 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'.

>
> 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;
> >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.

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 ...

--
Eric Sosman
lid

Eric Sosman, Apr 8, 2007
17. ### Karl UppianoGuest

"Mark Space" <> wrote in message
news:kqbSh.13036\$...
> MS wrote:
>> 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.
>>

>
> 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.

Karl Uppiano, Apr 8, 2007
18. ### =?ISO-8859-1?Q?Arne_Vajh=F8j?=Guest

Mark Space wrote:
> 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

=?ISO-8859-1?Q?Arne_Vajh=F8j?=, Apr 8, 2007
19. ### MSGuest

Eric Sosman emailed this:
> MS wrote:
>> 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.

>>
>> 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.

Horray !!

>> 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

>
> 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
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.

MS, Apr 8, 2007
20. ### Patricia ShanahanGuest

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

Patricia Shanahan, Apr 8, 2007