Page 1 of 2

[NXC] floor function not working!!!!

Posted: 30 May 2011, 15:27
by nxtboyiii
Hello,
As I was creating game I needed a float to round to the floor.
I made a test code:

Code: Select all

float x;
float y;
float ix;
float iy;
task main()
{
 while(1)
 {
   x+=0.2;
   ix=floor(x);
   NumOut(0,0,ix);
   NumOut(0,8,x);
   Wait(600);
 }
}
If you run the program, you will notice that the number above the bottom number will add by 0.2 every 600 milliseconds. Also the bottom number will show the floor of the top number. It works on some numbers but sometimes it thinks that the floor of, lets say, 2 is 1, which is incorrect, but when 2 turns to 2.2 then it says that the floor of 2.2 is 2, which is correct.
Can someone help me?

Is there a round function(nearest 100th,10th,etc)?
If not, can someone show me how?

I will just show the entire game code:

Code: Select all

int map[10][6];
float fmapx,fmapy;
int mapx,mapy;
bool gl;
int x,y;
int padd;

#define EPSILON 0.125 // Make this "as small as possible", and make sure it's a power of 2.
                      // Try 0.015625, but that may fail with larger numbers.

inline long myfloor(float x)
{
    return(x + EPSILON);
}

task dostuff()
{
 while(1)
 {
  RectOut(x,y,9,9,DRAW_OPT_FILL_SHAPE | DRAW_OPT_CLEAR);
  RectOut(0,40,100,8,DRAW_OPT_FILL_SHAPE | DRAW_OPT_CLEAR);
  if(SENSOR_1)
  {
   StopAllTasks();
  }
    if(fmapy == myfloor(fmapy))
  {
   padd=0;
  }
  else
  {
   padd=1;
  }
  if(ButtonPressed(BTNLEFT,0) && ((map[mapx-1][mapy] == 0 && map[mapx-1][mapy+padd] == 0) || fmapx > myfloor(fmapx)))
  {
 //  until(!ButtonPressed(BTNLEFT,0));
 //  PlayToneEx(500,30,1,0);
   fmapx-=0.2;
   x-=2;
   gl=1;
  }
 // mapx=(fmapx-frac(fmapx));
/*  if(fmapx == myfloor(fmapx))
  {
   gl=0;
  }*/
  if(ButtonPressed(BTNRIGHT,0) && map[mapx+1][mapy] == 0 && map[mapx+1][mapy+padd] == 0)
  {
   fmapx+=0.2;
   x+=2;
  }
  if(fmapx == myfloor(fmapx))
  {
   PlayToneEx(700,10,1,0);
   padd=0;
  }
  else
  {
   padd=1;
  }
    if(ButtonPressed(BTNCENTER,0) && map[mapx][mapy+1] == 0 && map[mapx+padd][mapy+1] == 0)
  {
   fmapy+=0.2;
   y+=2;
  }
      if(ButtonPressed(BTNEXIT,0) && ((map[mapx][mapy-1] == 0 && map[mapx+padd][mapy-1] == 0) || fmapy > myfloor(fmapy)))
  {
   fmapy-=0.2;
   y-=2;
  }
  mapy=myfloor(fmapy);
   mapx=myfloor(fmapx);
   RectOut(x,y,9,9,DRAW_OPT_FILL_SHAPE);
   NumOut(0,40,mapx);
   NumOut(15,40,fmapx);
   //NumOut(40,40,(frac(fmapx)));
   NumOut(70,40,myfloor(5));
   Wait(40);
 }
}

task draw()
{
 while(1)
 {
  for(int c=0; c < 10; c++)
  {
   for(int d=0; d < 6; d++)
   {
    if(map[c][d] == 1)
    {
     RectOut(c*10,d*10,9,9);
    }
   }
  }
 }
}

task main()
{
 SetAbortFlag(BTNSTATE_NONE);
 SetSensorTouch(S1);
 mapx=1;
 mapy=1;
 fmapx=1.0;
 fmapy=1.0;
 x=10;
 y=10;
 for(int c=0; c < 10; c++)
 {
  map[c][0]=1;
 }
  for(int c=0; c < 6; c++)
 {
  map[0][c]=1;
 }
  for(int c=0; c < 10; c++)
 {
  map[c][5]=1;
 }
  for(int c=0; c < 6; c++)
 {
  map[9][c]=1;
 }
 map[4][3]=1;
 Precedes(draw,dostuff);
}

If you run it you will encounter some problems because the floating-point values aren't exactly correct. Help? :)

Thanks,
nxtboy III

Re: [NXC] floor function not working!!!!

Posted: 30 May 2011, 19:08
by linusa
Two things come to my mind:
1.) I don't see a ClearScreen(). You should insert one to avoid the typical "what's displayed on the screen is not what's in my variable" bug.
2.) Not sure if that's the case here, BUT: Floating point arithmetics! If you think you have the number 2, it might actually be something like 1.999327 in memory. Or you might not add up exactly 0.2, but 0.199534 every time.

You generally want to avoid such round-off errors by NOT constantly adding up small numbers, but by multiplying them with a count. I.e. for certain things (such as rotation matrices from rotation angles, to give a common example), you DON'T want to add up 0.01 for 100 times, but rather calculate "100 * 0.01".

I'm not sure if you've hit this specific problem in this case (it usually doesn't show up -- when you're a bit lucky), but what I've said is true in general...

Re: [NXC] floor function not working!!!!

Posted: 30 May 2011, 23:31
by nxtboyiii
So how exactly would I fix it?

Could you give me some code?

Re: [NXC] floor function not working!!!!

Posted: 31 May 2011, 01:06
by muntoo
Try this:

Code: Select all

#define EPSILON 0.125 // Make this "as small as possible", and make sure it's a power of 2.
                      // Try 0.015625, but that may fail with larger numbers.

inline long myfloor(float x)
{
    return(x + EPSILON);
}

myfloat(41.875) == 42
If the distance between the next closest number is less than or equal to EPSILON, round up. The smaller EPSILON is, the more "accurate" it is. But making it smaller also creates your "problem". There's no perfect answer to this; unless, of course, you stored all numbers in a string. (But that's terribly slow, so no one in their right mind uses strings for numbers.)

-----

Read up on FP Comparisons. Maybe you can steal GNU's implementation. (The algorithm isn't necessarily theirs, though.)

Also, if you're using it for game programming, do you really need floor() to be that accurate? In fact, using floor like that will only increase your errors in the long run. (It's fine if you're using it a few times, but the error compounds if you keep doing it.)

You may wish to look into RK4. (gafferongames has some great game physics tutorials. Even StackOverflow thinks so!)

(@doc-helmut: You challenged me [a long, long time ago in a galaxy far, far away] that RK4 couldn't be NXC-ized. This should be close enough.)

Re: [NXC] floor function not working!!!!

Posted: 31 May 2011, 01:33
by nxtboyiii
Thanks Muntoo!!!!!!!!!
It worked!!! :D * 1,000,000

EDIT: Is there a round function?(round to the nearest tenth, hundredth,etc)
If not, can someone show me how?

Re: [NXC] floor function not working!!!!

Posted: 01 Jun 2011, 14:22
by nxtboyiii
Can anybody help? I was wondering how to make a function that rounds a number to the nearest tenth.

Re: [NXC] floor function not working!!!!

Posted: 01 Jun 2011, 14:25
by linusa
Multiple the number in question by 10, use your existing method (to round to the nearest integer), and then divide the resulting number by 10.

Re: [NXC] floor function not working!!!!

Posted: 01 Jun 2011, 15:53
by nxtboyiii
Hopefully that will work so the float won't be messed up. I will try it later today.

Like this:

Code: Select all

float roundto10th(float x)
{
 float mul=x*10;
 int mfloor=mul;
 float output=mfloor/10;
 return(output);
}
EDIT: afanofosc do you think you could help me on this?

Re: [NXC] floor function not working!!!!

Posted: 01 Jun 2011, 16:21
by linusa
In that case, don't forget the + 0.5, otherwise you're not rounding:

Code: Select all

int mfloor=mul+0.5;

Re: [NXC] floor function not working!!!!

Posted: 01 Jun 2011, 16:26
by HaWe
... or -0.5 for negative numbers...
but nxtboyiii,
if you want to round, why don't you use the round() function instead of floor() and ceil() ?