## C Logic

16 posts / 0 new
Author
Message
```#include <stdio.h>

int main(void)
{
float n;
do
{
n = get_float("Change owed: \n");
}
while(n<0.0);

int quarter=0, dime=0, nickel=0, penny=0;

while(n>=0.25)
{
quarter++;
n=n-0.25;
}
while(n>=0.10)
{
dime++;
n=n-0.10;
}
while(n>=0.05)
{
nickel++;
n=n-0.05;
}
while(n>=0.01)
{
penny++;
n=n-0.01;
}

printf("%i\n", (quarter + dime + nickel + penny));
}```

I have to calculate the least number of quarters, dimes, nickels and pennies which need to be handed out as change for a given amount.

For eg, 0.42\$ will require a quarter, a dime, a nickel, and two pennies. The output will be 5.

My code works fine for quarters, dimes, and nickels but gives a value one less than required only in the case of pennies. Suppose I input 0.02\$ the output is 1 whereas it should be 2.

The do-while loop asks the user for a value repeatedly until a positive value is entered.

There's a very good chance this is related to floating point imprecision.  When a user types 0.02 it may get translated to the floating point value 0.0199999.  Use an integer to represent the input value in pennies.  Round up the input float value by half a penny before converting to an integer (int pennies = (int)(100.0 * (input_float_value + 0.005)))

You could also just round up the float value and use it directly in your code.  I don't think it's the preferred approach, but it should work.

Last Edited: Mon. Oct 22, 2018 - 06:33 PM

A float is not an exact representation of a real number so you may

have 0.0099 left and this rounds down to 0 pennies.

It would be better to multiply your input by 100 and use integers:

`long n = 100 * (0.005 + get_float ("Change owed: \n"));`

The 0.005 is added to round up to the nearest cent.

--Mike

EDIT: I was too slow!

Last Edited: Mon. Oct 22, 2018 - 06:40 PM

Yes, the code works now. Thank you. But why was there a problem only in the case of the pennies? If I entered 0.25, 0.10, or 0.05 i got correct values

vcoder wrote:

Yes, the code works now. Thank you. But why was there a problem only in the case of the pennies? If I entered 0.25, 0.10, or 0.05 i got correct values

Some values may be translated into an exact floating point representation, while others may be off by a tiny bit (tiny probably being 1 LSB in the mantissa).  Try printing out the values as they are entered, and you should see the errors.

avr-mike wrote:

EDIT: I was too slow!

But you were correct, and that counts for a lot! Why work with floats here?

It seems much more logical to use an int and count in cents.

That way there are no rounding errors.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Way back when, one of the first programming classes I took taught all about these problems.  In essence, never assume that the floating point representation of X is exactly correct, and never assume that any mathematical operation (especially subtraction) on two floats is exactly correct.

Last Edited: Mon. Oct 22, 2018 - 07:24 PM

CompSci 101  seems to be a lost art!

so in binary values are represented in base 2,    16, 8, 4, 2, 1, 0, 1/2, 1/4, 1/8, 1/16, 1/32.....

with paper and pencil, try to figure out using the sequence above how many bits it takes to represent 0.01, or even 0.1, or 0.001....

Then you will know the root of the problem...

Jim

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

Last Edited: Mon. Oct 22, 2018 - 08:21 PM

"C Logic", eh?  Some of the 'Freaks that frequent the site would call that an oxymoron.

In essence, OP's quest is exactly the same as integer division by subtraction of powers of 10.  Same code could actually be used, with a modification of the table of values.  That would be especially useful e.g. when one or more of the change reservoir values goes empty.

```//
// **************************************************************************
// *
// *		U T O A Z
// *
// **************************************************************************
//
//
//	Transform a 16-bit unsigned value into unpacked ASCII.
//
//	Only the significant characters are output.  The number is right-justified.
//
//	There are 5 characters output with this version.
//
//	The resulting string is NOT null-terminated.

eeprom unsigned int	powers[] = {10000, 1000, 100, 10};

void						utoaz				(	unsigned char *outstring,
unsigned int number,
unsigned char zero)
{
unsigned int		work;
unsigned char		digit;	// this BCD digit to be output
unsigned char		index;	// which character are we working on.
unsigned int		power;

index = 0;				// start at the left-most character

work = (unsigned int) number;

for (index = 0; index < 4; index++)
{

digit = 0;
power = powers

;
while (work >= power)
{
work -= power;
digit++;
}
zero += digit;

if (zero > 0)
{
*outstring++ = digit | 0x30;
}
else
{
*outstring++ = ' ';
}
}

*outstring++ = ((unsigned char)work) | 0x30;  // last character, even if 0.

}
```

Well, not 'exactly'...

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.

Last Edited: Mon. Oct 22, 2018 - 09:28 PM

Just to point out that when you are using decimals I predict you cannot tell me *exactly* what 1/3 is in decimal?

Well the same is true when you count in binary. What we know as 1/10 (aka 0.1) simply can't be represented in binary in the same way that 1/3 (0.333333333....) cannot be represented in decimal.

As humans have a habit of counting in 100s, 10s, 1s, 1/10ths, 1/100ths, etc this is a bit of a problem!

Why on earth would you use floats?  It should be integers (cents).  You almost never would use floats with an AVR, if you were more than half-awake.  Floats have their place; usually coupled with being too much in a hurry, or too lazy to think things through.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

avrcandies wrote:

Why on earth would you use floats?  It should be integers (cents).  You almost never would use floats with an AVR, if you were more than half-awake.  Floats have their place; usually coupled with being too much in a hurry, or too lazy to think things through.

Sometimes I think there should be a \$5 charge for every float variable in every 8-bit app.  The only hangup is that I'm not sure how I'd collect the money.

kk6gm wrote:
The only hangup is that I'm not sure how I'd collect the money.
Maybe in the change. Ross McKenzie ValuSoft Melbourne Australia

See my tag-line below.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

kk6gm wrote:
In essence, never assume that the floating point representation of X is exactly correct, and never assume that any mathematical operation (especially subtraction) on two floats is exactly correct.
The difference between two floats often is exactly representable.

If they are within a factor of two of each other, the difference will be exact.

The problem is GIGO.

If one of the original numbers is not what you want,

the difference will not be either, and the relative "error" will be at least doubled.

Do not use sqrt(1+x)-1 on small x.

An algebraically equivalent formula produces more accurate results.

Moderation in all things. -- ancient proverb