Chief Delphi

Chief Delphi (http://www.chiefdelphi.com/forums/index.php)
-   Programming (http://www.chiefdelphi.com/forums/forumdisplay.php?f=51)
-   -   Strange products upon running code... (http://www.chiefdelphi.com/forums/showthread.php?t=39257)

neilsonster 07-08-2005 02:35

Strange products upon running code...
 
Hi, I have been attempting to write up lessons for next year's programmers to practice with. The last problem I made was to write a function that measures distance driven using encoders and display the data to the screen (I'm using a 2004 RC and MPLAB 7.00). In writing the code myself I ran into something strange.

Specifically the problem came up in calculating the wheel circumference. It was a very simple calculation and it spit out 0 for the circumference upon running the program. I then toyed around with just multiplying some random numbers and 15x30 came out to be -62 somehow. Here is the actual line of code that has caused me the trouble:

unsigned int WheelCirc = (2000 * WHEEL_RAD * PI) / 1000000;

In the above I was trying to stay away from using a float or double number obviously. I used 100 for WHEEL_RAD and 3142 for PI. The answer should be 628 but when I display the value to the terminal window it says 0. I then figured it must have somehow gotten a very small number (compared to 1 000 000) for (2000 * WHEEL_RAD * PI) to come up with 0, which doesn't make sense. Sure enough it ends up getting an answer of -24704 for the product of (2000 * WHEEL_RAD * PI), which would be the cause of the 0.

I then tried just writing out the numbers themselves and multiplying them for the WheelCirc variable and got the same thing. I tried it as a float rather than int and did 200*3.142 with no success (the answer came up as 17437). Has anyone else experienced this with the robot controller, MPLAB, or even in just any C program? Am I missing something (I'm hardly an expert at this)? If you haven't experienced this please give it a try and let me know if you get the same odd products. It's really cramping my style :(.

sciguy125 07-08-2005 03:35

Re: Strange products upon running code...
 
It looks like your variable is overflowing. Check how big an unsigned int is. I have a feeling that it's only 16 bit, which is only good for 65k. Your 2000 * WHEEL_RAD * PI is over 600 million.

sizeof(unsigned int) would give you the byte size of an unsigned int. I'd try it myself, but I don't have access to the compiler at the moment.

Mike Betts 07-08-2005 05:47

Re: Strange products upon running code...
 
Quote:

Originally Posted by neilsonster
...
Here is the actual line of code that has caused me the trouble:

unsigned int WheelCirc = (2000 * WHEEL_RAD * PI) / 1000000;
...

Andrew,

Try:

unsigned int WheelCirc = ((long) WHEEL_RAD * 710) / 113;

Note that the above will still fail if WHEEL_RAD gets too large...

Mike

neilsonster 07-08-2005 10:46

Re: Strange products upon running code...
 
Quote:

Originally Posted by Mike Betts
Andrew,

Try:

unsigned int WheelCirc = ((long) WHEEL_RAD * 710) / 113;

Note that the above will still fail if WHEEL_RAD gets too large...

Mike

That worked! Thanks!! *looks at your signature* I'll keep the 355/113 trick in mind :). One question though, what happens when you put the (long) in front of WHEEL_RAD?

Billfred 07-08-2005 10:56

Re: Strange products upon running code...
 
Quote:

Originally Posted by neilsonster
One question though, what happens when you put the (long) in front of WHEEL_RAD?

In essence, it forces the compiler to treat WHEEL_RAD like a long variable. (There was a post a few days ago in another thread that went a little deeper in depth, but I just woke up. >_<)

EricS-Team180 07-08-2005 17:47

Re: Strange products upon running code...
 
Quote:

Originally Posted by Billfred
In essence, it forces the compiler to treat WHEEL_RAD like a long variable.

So right you are BillFred! (long) is a c "unary" operator called a cast. It forces or "coerces" a type conversion.

...if you can, take a peek at a c manual like K&Rs "The c programming Language" ... p 46-47

In your original equation,
unsigned int WheelCirc = (2000 * WHEEL_RAD * PI) / 1000000;
the type conversion takes place across assignments, that is, the value of the right side is converted to the type of the left side. An unsigned int in the pic is 16 bits, so you get to play with 0-65535. Unfortunately, your values take you way outside this range. But with Mikes eq:
unsigned int WheelCirc = ((long) WHEEL_RAD * 710) / 113;

the cast, (long) forces the right hand side calculations to be done in 32 bits.
Then, the assignment (the = sign) forces a type conversion to 16 bits.

The higher order bits get chopped when "shortening" - going from long to unsigned int, but the value of the right hand side, now fits 0-65535, so you get the answer you expect.

(I also like the 355/113 trick :cool: )

Eric

Ryan M. 07-08-2005 20:25

Re: Strange products upon running code...
 
Quote:

Originally Posted by EricS-Team180
(I also like the 355/113 trick :cool: )

Hm... so explain this 355/113 thingy for me. I see a 113 on the bottom of the equation, but where's the 355 come in? (Besides his sig. ;))

Just curious.

Manoel 07-08-2005 20:37

Re: Strange products upon running code...
 
Quote:

Originally Posted by Ryan M.
Hm... so explain this 355/113 thingy for me. I see a 113 on the bottom of the equation, but where's the 355 come in? (Besides his sig. ;))

Just curious.

710 is 2 * 355 ;)

Mike Betts 08-08-2005 04:19

Re: Strange products upon running code...
 
Back in the late 1970's and early 1980's (when I was in college) you learned a lot of tricks to make integer arithmetic work efficiently*.

Remember the Pi approximation by remembering the sequence 113355. Split into two different 3-digit integers and ask yourself if Pi is greater than or less than one? Voila! Pi = 355/113

Nowadays, there are tools to assist with computing ratios. I use Mathcad which will compute ratios and allow you to adjust for accuracy. As examples which are much harder to remember than Pi:

Sqrt(2) = 239/169 (or 577/408)
e = 193/71 (or 1264/465)

When applying any ratio in embedded systems, remember to multiply first and then divide and to watch for numeric overflows...

By the way, this numerical approximation was "discovered" over 2500 years ago (Google: Zu Chongzi or 113355)... All we had to do was to learn to apply it.

Mike

* The first computer I built from scratch had a Z80 (8-bit) processor running at 3.579545 MHz (NTSC color burst frequency). It took minutes to do any non-trivial math operations...

Ryan M. 08-08-2005 07:22

Re: Strange products upon running code...
 
Thanks, Mike.

Hutch 08-08-2005 09:00

Re: Strange products upon running code...
 
Now, I've got to ask, why aren't you just using a float? I know they are emulated; I know that should make them slow. But chances are, you won't have any speed issues - and in exchange, you remove a lot of the overflow problems.

Mike Betts 08-08-2005 10:12

Re: Strange products upon running code...
 
Quote:

Originally Posted by Hutch
Now, I've got to ask, why aren't you just using a float? I know they are emulated; I know that should make them slow. But chances are, you won't have any speed issues - and in exchange, you remove a lot of the overflow problems.

Hutch,

Please read this...

Mike

Alan Anderson 08-08-2005 10:24

Re: Strange products upon running code...
 
Quote:

Originally Posted by neilsonster
I then toyed around with just multiplying some random numbers and 15x30 came out to be -62 somehow.

You've already gotten a solution, so I'll just explain the underlying problem in more detail.

When dealing with constants, C compilers typically treat them as the smallest type that can hold a particular value. In this case, both 15 and 30 can fit in a signed char, which can range from 0 to 255. Arithmetic expressions are also evaluated using the smallest type possible, with any overflows in intermediate or final results simply being ignored.

When you multiply 15 by 30, you get the answer 450. When a C program does it, here's what happens.

15 decimal = 00001111 binary
30 decimal = 00011110 binary
product = 111000010 binary
Only eight bits of the product will fit in the result, giving 11000010 binary, which is -62 decimal in a signed char.

That's one of the things I dislike most about using C in general, and the Microchip compiler in particular. The programmer has to keep track of how large intermediate results might be, and deal with the potential overflows explicitly.

Greg McCoy 08-08-2005 12:32

Re: Strange products upon running code...
 
Quote:

Originally Posted by Mike Betts
Nowadays, there are tools to assist with computing ratios. I use Mathcad which will compute ratios and allow you to adjust for accuracy.

I just wanted to point out that the TI-89/92 graphing calculator family can also do this with the exact() function:

exact(3.14159265358, 0.00001) = 355/113

The second argument is the tolerance. I'm not sure what other models (if any) are capable of this.

Hutch 08-08-2005 22:04

Re: Strange products upon running code...
 
Quote:

Originally Posted by Mike Betts
Hutch,

Please read this...

Mike

I would never say to use the built in sine function... I don't even use it on desktop computers (I do a lot of work with real time 3D graphics). A simple approximation will handle sine quite readily. With robots, you can even get away with a 7th order taylor series if you don't want to write any sort of decent approximation - physical innacuracies will be greater than approximation inaccuracies then.

And I'm guessing that normal stuff like adds and multiplies are relatively cheap for floats, even on this hardware... Which, in my opinion, makes them pretty much the way to go. While you may get a little more speed with integers, I've never had a problem with the controller's speed and it not finishing the slow loop in 26.2ms while doing normal robot stuff. And using floats saves you that 2K look up table.

Alan Anderson 09-08-2005 10:29

Re: Strange products upon running code...
 
Quote:

Originally Posted by Hutch
And I'm guessing that normal stuff like adds and multiplies are relatively cheap for floats, even on this hardware... Which, in my opinion, makes them pretty much the way to go.

While you're certainly entitled to your opinion, you should be aware that it is based on a guess and could easily have little to do with reality. With the tools we're given, floating point operations on a PIC are very expensive.
Quote:

While you may get a little more speed with integers, I've never had a problem with the controller's speed and it not finishing the slow loop in 26.2ms while doing normal robot stuff. And using floats saves you that 2K look up table.
On the other hand, a floating-point library itself eats up a lot of program space. If you just want to eliminate the trig lookup table, using a CORDIC algorithm is probably the most appropriate tactic.

Hutch 09-08-2005 18:41

Re: Strange products upon running code...
 
I think we have different definitions of fast: mine is that it works within the time allowed. It may not be the fastest it could possibly be, but it's good enough.

I had forgotten that the floating point library takes up space - thanks for pointing that out.

The solution of using floats is still far more elegant and easy to understand than some integer hacks and a look up table, and I feel that it's more important to have code that's easy to understand and maintain than it is to optimize something that (at least for me), doesn't need optimization. It's the same reason that I use std::string rather than C strings, even though there is a small performance penalty.

Gdeaver 10-08-2005 00:20

Re: Strange products upon running code...
 
One thing I'd like to point out that there is a strong tendency to apply continuous math to a digital or discrete system. In other words when dealing with microcontrolers without hardware floating point processors allot of algorithms can be better solved with matrix math. State machines are an example when the inputs just have a binary state. With analog to digital conversion there are a finite set of integer inputs. A solution matrix (table) can be built of the desired outputs. Usually its faster to parse the matrix and find the desired outputs than it is to use continuous math. It's not bad with 1 input and 1 output. As the number of inputs and outputs increase it gets harder but, still can be more productive than dealing with solving complex equations on a microcontroller.


All times are GMT -5. The time now is 00:13.

Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
Copyright © Chief Delphi