Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

C programming question

Status
Not open for further replies.

MrMikey83

New Member
I'm using Codevision AVR (C code) to program my Atmel uProcessors.

I'm trying to get the bugs out of a piece of code and realised that it is rounding decimal numbers (1.5 3.14159 etc) to the whole number. After finding this, I realized I don't have any idea how to keep the decimal spaces without rounding.

Any help is greatly appreciated!

~Mike
 
MrMikey83 said:
I'm using Codevision AVR (C code) to program my Atmel uProcessors.

I'm trying to get the bugs out of a piece of code and realised that it is rounding decimal numbers (1.5 3.14159 etc) to the whole number. After finding this, I realized I don't have any idea how to keep the decimal spaces without rounding.

Any help is greatly appreciated!

I can't help you specifically on C or Atmel, but generally it's a VERY bad idea to use floating point numbers, they are extremely slow, take large amounts of space, and are inaccurate.

Far better to scale the values to be integers, and add the decimal point afterwards - as an example, you don't use money as $1.23 - you use 123 cents instead.

But keeping on the rounding theme, you add 0.5 to the value, this rounds it to the nearest whole number - ASSUMING the maths routines are proper floating point!.
 
If it's rounding to whole numbers, then you aren't using float or double.

In addition to the speed issue, most floating point arithmetic uses binary fractions, so you will get binary rounding in most cases. Most decimal fractions cannot be stored exactly as binary fractions.
 
Ok, maybe It's right and my code for the LCD is wrong.
As a test, I put in dt = 3.14159 but the LCD shows me 4048 when I print dt.

Code:
	//Display on LCD
	lcd_clear();
	sprintf(lcd_out, "Pi = %5d", dt);
	lcd_puts(lcd_out);

Is my code for printing to the LCD wrong for this type of number?
~Mike
 
If dt is float or double, you should be using one of the floating-point conversion codes. They are %e, %f, and %g. %e converts the number to "E" format, %f converts to fixed point format, and %g, if it exists in your compiler, automatically chooses between the other two. Check your C manual for formatted I/O.
 
%e, %f, and %g don't work either.

Code:
float	dt;

	dt = 3.14159;
	//Display on LCD
	lcd_clear();
	sprintf(lcd_out, "Pi = %5e", dt);
	lcd_puts(lcd_out);

And what I get on the LCD screen is "Pi = 4048"
 
reply

you got 4048 in the first code because you specified %d which is used for integers. you could give us the whole code and tell us what you're expecting. 4048 seems to be too far off 3.142?

try using %f instead, if it doesn't work, check other parts of the code...
 
With %d, I get "Pi = 3" With %e, %f, and %g I get "Pi = 4048"

The rest of the code has to do with my ballancing bot. The Pi thing really has nothing to do with the bot, I am just trying to get this number to display so that I know it is calculating the right numbers without rounding them.
 
One way I can get a result of 3 for float dt and %d is if all numbers are stored Big-Endian. But that would also imply a non-IEEE floating-point format (because of the exponent range, not the byte ordering).

I would have expected the result to be the opposite, with %d showing a wild number, and the %e, %f, and %g showing a rounded result.

As an example of %f, you would normally use %8.3f to create at least 8 characters containing 3 decimal digits in the fraction.
 
As has been said, using float or double is a bad idea since they're slow as hell to do math on. Microcontrollers have no hardware resources to do floating point ops and performing them is like 1000x slower than integer ops.

Generally when we need to work with decimals on a controller, it is best to simply multiply it so the desired accuracy is achieved. For example unsigned int can store values 0-65535. You could deal with Pi as Pi*10,000, Pi*100, Pi*2^14, or Pi*2^8, etc. As always, you will need to evaluate your math carefully to ensure that nowhere will a math operation overflow the storage type being used. This is true regardless of whether a multiplier is used or not. Also research "integer promotion" which is how C normally does math, this will directly affect when a calculation will overflow.

Your display code will need to do some tricks to take 314159 and display as 3.14159. A string processing task could be told to make a string out of the number and add a decimal 5 places over.
Code:
void sprintdec(char *ptr, unsigned int number, unsigned char shift);
(of course you may also want signed integer, signed long, or unsigned long)

When stating this in your code you will probably want to take advantage of constant folding. For example:
Code:
#define DEC_MULT 10000
unsigned int myInt= 3.14159f * DEC_MULT;

The interesting part here is that the compiler will realize that myInt is just supposed to get assigned 31415 and will only produce assembly code that assigns this value; it will not create code that does a multiplication or floating point op. Note if there is a variable in this equation- such as if DEC_MULT were a variable- this will not be the case.
 
Thanks for the ideas. I think I'll just have to multiply the numbers and divide the results. When the robot is complete, I'm not going to have to display these numbers, I was just using the LCD for troubleshooting and seeing if I was getting the values close to what they need to be.
~Mike
 
Hi MrMikey,

Two things:

First, it's probably not a bad idea to put an F at the end of your floating point constant, ie

dt = 3.14159F;

more importantly, it is possible you do not have the floating point support enabled in the compiler options.

Look under Configure Project, C Compiler.
The default setting for sprintf support is int only - select 'float, width, precision' to get the floating point support in. When you do that, you will discover why floating point is such a bad idea - your code will grow quite a bit, as the floating point library gets pulled in.

Mike.
 
Hey Mike, that was the problem. My code isn't all the big, I have plenty of room on the chip. I'll probably use floating points to get it working first, then if its going to slow, I can convert it over to optimize the code when I get it running.
Thanks!
~Mike
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top