0

Possible Duplicate:
Java float division precision

I'm trying to fix a bug in one of my games, and need to calculate the optimum tile size for that. To do that, I get the device's screen resolution, number of rows and columns for that level, and then calculate the tile size which allows my game to use the maximum screen space. However, Android seems to be failing at basic math, losing precision on a simple division operation. Here's the appropriate code:

while(!foundIt)
    {
        int tileArea = boxes*i*i; //Boxes = 20*26
        float ratio = displayArea/tileArea;  //displayArea = width*height
        Log.d("Balance", displayArea + "/" + tileArea);
        Log.d("Balance", boxes + ", " + i);
        Log.d("Balance", "Ratio: " + ratio);
        int last = 0;
        if(ratio>1)
        {
            if(last==LAST_WAS_LESS_THAN_ONE)
            {
                i--;
                foundIt=true;
            }
            last = LAST_WAS_MORE_THAN_ONE;
        }
        else if(ratio==1)
        {
            Log.d("Balance", "Found a perfect fit!!");
            foundIt=true;
        }
        else if(ratio<1)
        {
            if(last==LAST_WAS_MORE_THAN_ONE)
            {
                i--;
                foundIt=true;
            }
            last = LAST_WAS_LESS_THAN_ONE;
        }
        if(!foundIt)
        {
            i++;
        }
    }

Here is a sample run on my Nexus S (Resolution: 480 by 800):

06-18 13:20:45.808: D/Balance(1069): Width: 480 Height: 800
06-18 13:20:45.808: D/Balance(1069): 384000/520
06-18 13:20:45.808: D/Balance(1069): 520, 1
06-18 13:20:45.808: D/Balance(1069): Ratio: 738.0
06-18 13:20:45.808: D/Balance(1069): 384000/2080
06-18 13:20:45.808: D/Balance(1069): 520, 2
06-18 13:20:45.808: D/Balance(1069): Ratio: 184.0
06-18 13:20:45.808: D/Balance(1069): 384000/4680
06-18 13:20:45.808: D/Balance(1069): 520, 3
06-18 13:20:45.808: D/Balance(1069): Ratio: 82.0
06-18 13:20:45.808: D/Balance(1069): 384000/8320
06-18 13:20:45.808: D/Balance(1069): 520, 4
06-18 13:20:45.808: D/Balance(1069): Ratio: 46.0
06-18 13:20:45.808: D/Balance(1069): 384000/13000
06-18 13:20:45.808: D/Balance(1069): 520, 5
06-18 13:20:45.808: D/Balance(1069): Ratio: 29.0
06-18 13:20:45.808: D/Balance(1069): 384000/18720
06-18 13:20:45.808: D/Balance(1069): 520, 6
06-18 13:20:45.808: D/Balance(1069): Ratio: 20.0
06-18 13:20:45.808: D/Balance(1069): 384000/25480
06-18 13:20:45.808: D/Balance(1069): 520, 7
06-18 13:20:45.808: D/Balance(1069): Ratio: 15.0
06-18 13:20:45.808: D/Balance(1069): 384000/33280
06-18 13:20:45.808: D/Balance(1069): 520, 8
06-18 13:20:45.808: D/Balance(1069): Ratio: 11.0
06-18 13:20:45.808: D/Balance(1069): 384000/42120
06-18 13:20:45.808: D/Balance(1069): 520, 9
06-18 13:20:45.808: D/Balance(1069): Ratio: 9.0
06-18 13:20:45.808: D/Balance(1069): 384000/52000
06-18 13:20:45.808: D/Balance(1069): 520, 10
06-18 13:20:45.808: D/Balance(1069): Ratio: 7.0
06-18 13:20:45.808: D/Balance(1069): 384000/62920
06-18 13:20:45.808: D/Balance(1069): 520, 11
06-18 13:20:45.808: D/Balance(1069): Ratio: 6.0
06-18 13:20:45.808: D/Balance(1069): 384000/74880
06-18 13:20:45.808: D/Balance(1069): 520, 12
06-18 13:20:45.808: D/Balance(1069): Ratio: 5.0
06-18 13:20:45.808: D/Balance(1069): 384000/87880
06-18 13:20:45.808: D/Balance(1069): 520, 13
06-18 13:20:45.808: D/Balance(1069): Ratio: 4.0
06-18 13:20:45.808: D/Balance(1069): 384000/101920
06-18 13:20:45.808: D/Balance(1069): 520, 14
06-18 13:20:45.808: D/Balance(1069): Ratio: 3.0
06-18 13:20:45.808: D/Balance(1069): 384000/117000
06-18 13:20:45.808: D/Balance(1069): 520, 15
06-18 13:20:45.808: D/Balance(1069): Ratio: 3.0
06-18 13:20:45.808: D/Balance(1069): 384000/133120
06-18 13:20:45.812: D/Balance(1069): 520, 16
06-18 13:20:45.812: D/Balance(1069): Ratio: 2.0
06-18 13:20:45.816: D/Balance(1069): 384000/150280
06-18 13:20:45.816: D/Balance(1069): 520, 17
06-18 13:20:45.816: D/Balance(1069): Ratio: 2.0
06-18 13:20:45.816: D/Balance(1069): 384000/168480
06-18 13:20:45.816: D/Balance(1069): 520, 18
06-18 13:20:45.816: D/Balance(1069): Ratio: 2.0
06-18 13:20:45.816: D/Balance(1069): 384000/187720
06-18 13:20:45.816: D/Balance(1069): 520, 19
06-18 13:20:45.816: D/Balance(1069): Ratio: 2.0
06-18 13:20:45.816: D/Balance(1069): 384000/208000
06-18 13:20:45.816: D/Balance(1069): 520, 20
06-18 13:20:45.816: D/Balance(1069): Ratio: 1.0
06-18 13:20:45.816: D/Balance(1069): Found a perfect fit!!

Calculating the ratio on a calculator reveals a loss of precision. Does anybody know why this is? Is something in my implementation wrong?

Community
  • 1
  • 1
Raghav Sood
  • 81,899
  • 22
  • 187
  • 195
  • Start here: http://en.wikipedia.org/wiki/Floating_point_arithmetic#Accuracy_problems – Nikolay Elenkov Jun 18 '12 at 08:00
  • 2
    Try this, instead of `float ratio = displayArea/tileArea;`, try casting the int to a float, like this, `float ratio = displayArea/(float)tileArea;` – Austin Jun 18 '12 at 08:00
  • Uh, @Austin that's exactly what I'm doing in the third line. – Raghav Sood Jun 18 '12 at 08:01
  • 1
    @RaghavSood Unless at least one of 'displayArea' and 'tileArea' is a float (which they aren't judging by your logs), that most certainly is NOT what you're doing. You're dividing one int with an other in, which always returns an int and assigning the result to a float. Do an explicit cast exactly as Austin suggested adding the cast to float to tileArea to divide an int with a float, which yields a float return value with more precision than you currently are getting. – Kallja Jun 18 '12 at 08:09
  • @Jarkko I posted that comment before Austing edited his comment to include the casting. I guess he forgot the first time. In any case, I tried it and it works, so Austin, if you could post that as an answer, I'll accept it as the solution. – Raghav Sood Jun 18 '12 at 08:13
  • @RaghavSood Oh, sorry about that. – Kallja Jun 18 '12 at 08:13
  • That's an exact duplicate : an answer isn't needed (but yes, if somebody can answer, that's Austin). – Denys Séguret Jun 18 '12 at 08:14

2 Answers2

2

From the print out, it seems that displayArea and tileArea are declared as int or long. And you are doing integer division (displayArea/tileArea) before the result is promoted to float type and assigned to ratio.

Since you are comparing whether one is larger than the other, why not doing the direct comparison between displayArea and tileArea? It is much better than fooling around with floating point operations.

nhahtdh
  • 55,989
  • 15
  • 126
  • 162
  • Your point about it being an integer division makes sense. However, I can't directly compare it, as I need to know when tileArea is closest to display area. Logically, whichever ratio is closest to 1 should have used the closest tileArea. – Raghav Sood Jun 18 '12 at 08:05
  • 1
    @RaghavSood: The type cast suggestions by Austin in the comment and answer by bmusical are actually work around for that. Since it will turn one of the number into floating point, floating point division will take place. – nhahtdh Jun 18 '12 at 08:10
0

Seems like Type Casting issue, Try casting your displayArea to float.

bmusical
  • 244
  • 3
  • 14