![]() |
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 :(. |
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. |
Re: Strange products upon running code...
Quote:
Try: unsigned int WheelCirc = ((long) WHEEL_RAD * 710) / 113; Note that the above will still fail if WHEEL_RAD gets too large... Mike |
Re: Strange products upon running code...
Quote:
|
Re: Strange products upon running code...
Quote:
|
Re: Strange products upon running code...
Quote:
...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 |
Re: Strange products upon running code...
Quote:
Just curious. |
Re: Strange products upon running code...
Quote:
|
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... |
Re: Strange products upon running code...
Thanks, Mike.
|
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.
|
Re: Strange products upon running code...
Quote:
Please read this... Mike |
Re: Strange products upon running code...
Quote:
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. |
Re: Strange products upon running code...
Quote:
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. |
Re: Strange products upon running code...
Quote:
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. |
Re: Strange products upon running code...
Quote:
Quote:
|
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. |
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