Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Rounding to nearest 0.05

Credits go to miran for helping out!
This thread is locked; no one can reply to it. rss feed Print
Rounding to nearest 0.05
Samuel Henderson
Member #3,757
August 2003
avatar

I'm trying to figure out how to round to the nearest nickel for a vending machine emulator I am working on.

I'm allowing the users to enter a markup percentage that basically raises the prices of all items in the machine by the given percentage.

the code I have for this is:

//It's one of those coffee vending machines :)
void HotLiquid::UpdatePrices(double MarkupPercent)
{
  price = (((MarkupPercent / 100)+1)*price);
}

So for example ... if the price was $0.50, and the markup is 25% the new price is $.063. I'm going to need to change that to 0.65 ..

Whats the best way do this?

=================================================
Paul whoknows: Why is this thread still open?
Onewing: Because it is a pthread: a thread for me to pee on.

miran
Member #2,407
June 2002

1. Multiply by 20
2. Round up to integer
3. Divide by 20
4. Profit! (literally)

EDIT: And code:

double round_to_nickel(double price) {
   return ceil(price*20.0)/20.0;
}

--
sig used to be here

Samuel Henderson
Member #3,757
August 2003
avatar

Thank you much Miran;D

=================================================
Paul whoknows: Why is this thread still open?
Onewing: Because it is a pthread: a thread for me to pee on.

Billybob
Member #3,136
January 2003

For nearest I believe you need:
return floor((price + 0.025) * 20.0) / 20.0;
Right?

EDIT: Spent a little time to make this puppy

// Rounds X to the nearest Y
double round(double x, double y)
{
  return floor(x / y + 0.5) * y;
}

Quite a work of art if I do say so myself.
Works on everything. You can round to the nearest million or nearest millionth. As long as double can contain the numbers.

Samuel Henderson
Member #3,757
August 2003
avatar

Yeah... I quickly discovered that if the price worked out to something like say 0.601 then it would jump to 0.65.

Final formula worked out to: price = floor(price*20.0 +.5)/20.0;

Which essentialy is the same as Will's.

Thanks again ;D

=================================================
Paul whoknows: Why is this thread still open?
Onewing: Because it is a pthread: a thread for me to pee on.

Arthur Kalliokoski
Second in Command
February 2005
avatar

Vending machine? No interest calculations? No multi million dollar transactions? Use ints for cents, when displaying, cast to float and divide by 100. EXACT arithmetic every time...

They all watch too much MSNBC... they get ideas.

kentl
Member #2,905
November 2002

Without using floor: :) (typecast in case price is not an int already)
price = ((int)price*20.0 +.5)/20.0);

[edit]: Moved in the type cast

Billybob
Member #3,136
January 2003

I wonder which is faster...not that it matters, just curious.
By the way your code is wrong. You need to move the int inward, so /20.0 is outside.

The casting method would result in two casts. One from float->int and then int->float. That compared to whatever floor does...

kentl
Member #2,905
November 2002

Quote:

I wonder which is faster...not that it matters, just curious.

Yeah it would be fun to know. I suspect that they would be equally fast, if floor() is implemented using a type cast and gets inlined during compilation.

Indeterminatus
Member #737
November 2000
avatar

If I remember correctly, how the cast double->int or float->int works is at least for C++ implementation-defined (i.e. does not have to be the same on all compilers), so it is a no-no for maximum portability. I could be wrong though, but I'm too lazy right now to look it up in the standard.

_______________________________
Indeterminatus. [Atomic Butcher]
si tacuisses, philosophus mansisses

kentl
Member #2,905
November 2002

All I ever heard is that all decimal digits are cut off. Which I know they are in at least GCC and Visual Studio (which makes for some pretty good portability).

These two sources says nothing about it being implementation specific:
http://www.cs.tut.fi/~jkorpela/round.html
http://www.eskimo.com/~scs/C-faq/q14.6.html

Arthur Kalliokoski
Second in Command
February 2005
avatar

floor() needs to do some special stuff so it goes to the larger abs int when negative, and smallest abs int when positive. Simply casting a float to an int takes up some fpu junk with control words etc. which is slow, and floor() and ceil() have to do that and more.

They all watch too much MSNBC... they get ideas.

Thomas Harte
Member #33
April 2000
avatar

Quote:

Whats the best way do this?

Other people have posted perfectly functional ways, but in my opinion the best way (which I consider to mean "neatest" and/or self-explanatory) is with fmod, e.g.
if(a = fmod(b, 0.05)) b += 0.05 - a;
Anyone any idea on the relative efficiency of that?

Billybob
Member #3,136
January 2003

Thomas: That doesn't round, it rounds up only.

I did a couple tests out of curiosity and came up with:

j up to 1
gcc round_test.c -o round_test.exe:
round1: 12s, 11s, 11s, 11s
round2: 4s, 4s, 4s, 4s

Modify to run j up to 100
gcc round_test.c -o round_test.exe -O3 -s
round1: 13, 13, 14, 13
round2: 13, 13, 13, 13

Therefore: Under no optimisations my algorithm is about 3x worse than Kent's. Under optimisations the two algorithms are exactly the same, running at about 769 million calls per second with the for loops and floating point additions.
AMD 64+ 3000
GCC 3.4.2
Gobs of stuff in the background ;D

1double round1(double x, double y)
2{
3 return floor(x / y + 0.5) * y;
4}
5 
6double round2(double x, double y)
7{
8 return ((int)(x / y +0.5)) * y;
9}
10 
11int main()
12{
13 int start = time(0);
14
15 double r = 0.0;
16 double q = 0.5;
17 int i, j;
18 for(i = 0; i < 100000000; ++i)
19 {
20 for(j = 0; j < 100; ++j) // Used to modify -O3 tests, under no optimisations leave at j < 1;
21 {
22 round2(r, q);
23 r += 0.01;
24 q += 0.01;
25 }
26 }
27
28 int end = time(0);
29 int total = end - start;
30 printf("%i seconds\n", total);
31
32 return 0;
33}

Thomas Harte
Member #33
April 2000
avatar

Quote:

Thomas: That doesn't round, it rounds up only.

You're right - I cannot read. So:
if((a = fmod(b, 0.05)) > 0.025) b += 0.05 - a; else b -= a;
it is. Although I have the feeling I've somewhat lost the readability edge and any arguable claim to having the "best" method.

Here's a spot fact: the American currency is one of the few not to obey the "halves and doubles of powers of ten" rule, making it a little harder to divide quantities into coins for Americans!

Paul Pridham
Member #250
April 2000
avatar

Here's a simple rounding function I use for some turtle graphics stuff I wrote.

double round(double val)
{
   val*=1000;
   val=(int)(val>=0?val+0.5:val-0.5);
   val/=1000;
   return val;
}

Tobias Dammers
Member #2,604
August 2002
avatar

What Arthur said: ints for cents.
Rounding to nearest nickel:

int round_to_nickel(int cents) {
  int mr = cents % 5;
  return ((mr > 2) ? (cents - mr + 5) : (cents - mr));
}

There, a single integer modulo, one check, one sub, and one optional add. And perfect accuracy (although negative numbers will need special care).

Splitting into dollars and cents:

void cents_to_dollars_and_cents(int v, int& dollars, int& cents) {
  cents = c % 100;
  dollars = c / 100;
}

IIRC, libc has a special function to do both the mod and the div in one step, but I'm too lazy to look it up right now.

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

Go to: