Floating point make me sink! (also sick) [solved]

Go To Last Post
16 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have put these line which are supposed to "make my life easier" when I switch clock frequency and still want to keep the same backgroung timer ticks of 10ms in this case.

#define clock_divisor 1024
#define tick_time 10		// In ms
	
	OCR2A = (uint8_t) ((tick_time / 1000) / ((uint32_t) (1/F_CPU) * (uint32_t) (clock_divisor)));

It doesn't work, of course! :evil: tried various formats which work correctly with my calculator but I always get a "divide by 0" warning.

Tried loading up all the floating point libraries too. Am I missing something in the linker settings? At the moment I have nothing there but have tried the same "magic words" I use for floating point printf without any imrpovement.

In the simulator a simple 11/2 gives me a result of 5 (watch window) rather than 5.5 for a variable which is declared as float. So something is not well somewhere.

ARGGGHHH just realised I have spend 2 hours "making my life easier" :twisted:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

Last Edited: Wed. Feb 1, 2012 - 06:31 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

All those integer calculations are done by the preprocessor and the brackets force the evaluation order.

so ;
(tick_time/1000) becomes (10/1000) -> 0 and
(1/F_CPU) -> 0

Rearrange the formula to ;
( tick_time * F_CPU ) / ( 1000 * clock_divisor )

The multiplied values might produce internal overflow and you then need to typecast some of the #defined values to a larger-capacity integer.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Rearrange the formula to ;
( tick_time * F_CPU ) / ( 1000 * clock_divisor )

Thanks Mike, I now get:

../Flasher_init.c:42: warning: integer overflow in expression

What I don't understand is why the calcs are being done in integer rather than floating point as they should.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now seeing the trees instead of the forest I have

	OCR2A = ( tick_time * (F_CPU /1000) ) / ( clock_divisor );

and it seems to work in the simulator, let's try the real thing now.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Success, thanks. Amazing what happens when soemone else has a look at something one gets stuck with for hours. :oops:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
What I don't understand is why the calcs are being done in integer rather than floating point as they should.
The preprocessor defaults to integer operations, to force it to do floating-point you need to make one (or more) of the operands a float, eg 1000.0

Last Edited: Wed. Feb 1, 2012 - 07:23 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Haha I was just declaring the variables float but using whole numbers. Learned something new, so not a complete waste of 2 hours. :)

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually, it's not the preprocessor.

The preprocessor does not do any calculations (except in conditional expressions, i.e. in #if(expression) and even that's quite limited and tricky), it only processes the text.

And it does not matter whether the calculations are performed compile-time or run-time; the point is how the numeric constants are interpreted. In C, they do have an implicit type, depending on how you write it (e.g. if you want a floating point constant it has to contain either "." or "E"); and then typing of operands influences how the arithmetic operations are performed.

The standard may be worth consulting when in doubt.

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

John,

As a rule of thumb I just simply add .0 to any whole number constant I know is being used as float. In fact if it's a float not a double (not that this matters on AVR) use .0f

So:

float foo = 13;

becomes

float foo = 13.0f;

(and again it makes no difference in this assignment but just do it consistently and you will be fine).

The other day someone was talking about using:

n = 5 / 1000 / 0.01;

I'd make that either just:

n = 5.0 / 1000.0 / 0.01;

or

n = 5.0f / 1000.0f / 0.01f;

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

John,

I think you will find the same rules apply with your Assembler.

Let's face it, you would want to do exactly the same calculation for OCR2A in an ASM program. More often you calculate a a 16-bit expression that you put in an .EQU or .SET statement. Then you use that value in OCR1AH and OCR1AL statements. e.g.

    .equ   CTCvalue = (F_CPU / 2 / prescale / frequency)
    ...
    ldi r24,(CTCvalue >> 8)
    out OCR1AH,r24
    ldi r24,(CTCvalue & 0xFF)
    out OCR1AL,r24

Try it and see. Your ASM listing will show you the value in the .SET statement.
You can force f-p arithmetic by just making one of the ingredients f-p. e.g. 2.0 or prescale=1024.0

You can also do rounding very easily by simply adding 0.5

Untested. You can try it for yourself.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hehe

I always get tripped up on the idea that in integer math 5/2 = 3. I always thought it was 2 remainder 1. Catches me every now and then when I have to write code for accountants who count pennies.

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Torby wrote:
hehe

I always get tripped up on the idea that in integer math 5/2 = 3. I always thought it was 2 remainder 1. Catches me every now and then when I have to write code for accountants who count pennies.


???
Surely 5/2 in integer maths is truncated to 2?

Four legs good, two legs bad, three legs stable.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try it in SQL

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Now seeing the trees instead of the forest I have
Code:
OCR2A = ( tick_time * (F_CPU /1000) ) / ( clock_divisor );
and it seems to work in the simulator, let's try the real thing now.

See how exact it is with a clock speed of 3.6864MHz. (Don't do the divide first...)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry guys, been a little busy. :-)

Modified the code again and by simply defining the time in floating point format (0.01 instead of 10 and then dividing by 1000) it seems to have fixed the issue...again. So now I have

#define clock_divisor 1024
#define tick_time 0.01		// In seconds
	OCR2A = (tick_time * F_CPU) / clock_divisor;

Quote:
See how exact it is with a clock speed of 3.6864MHz
The way I had it before the value comes up at 35, the way I have it now it comes up as 36, the same as what my calculator comes up with.

Tried with a few F_CPU, time and clock_divisor values and it all seems to work.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The way I had it before the value comes up at 35, the way I have it now it comes up as 36, the same as what my calculator comes up with.

Exactly my point on "the way you did it before".

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.