Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find the nearest nice number

Given a base currency of GBP £, and a table of other currencies accepted in a shop:

Currency       Symbol     Subunits      LastToGBPRate
------------------------------------------------------
US Dollars     $          100           0.592662000
Euros          €          100           0.810237000
Japanese Yen   ¥          1             0.005834610
Bitcoin        ฿          100000000     301.200000000

We have a working method that converts a given amount in GBP Pence (AKA cents) into Currency X cents. Given a price of 999 (£9.99), for the above currencies it would return:

Currency       Symbol
---------------------
US Dollars     1686
Euros          1233
Japanese Yen   1755
Bitcoin        3482570  

This is all working absolutely fine. We then have a Format Currency method which converts them all into nice looking numbers:

Currency       Formatted
---------------------
US Dollars     $16.86
Euros          €12.33
Japanese Yen   ¥1755
Bitcoin        ฿0.03482570  

Now the problem we want to solve, is to round these amounts to the nearest meaningful pretty number in a general purpose algorithm given the information above.

This serves two important benefits:

  • Prices for most currencies should appear static for visitors over short-medium term time frames
  • Presents the visitor with a culturally meaningul price point which encourages sales

A meaningful number is one where the smallest unit displayed isn't smaller than the value of say £0.10, and a pretty number is one which ends in 49 or 99. Example outputs:

Currency       Formatted         Meaninful and Pretty
-----------------------------------------------------
US Dollars     $16.86            $16.99
Euros          €12.33            €12.49
Japanese Yen   ¥1755             ¥1749
Bitcoin        ฿0.03482570       ฿0.0349 

I know it is possible to do this with a single algorithm with all the information given, but I'm struggling to work out even where to start. Can anyone show me how to achieve this, or give pointers?

Please note, storing a general formatting rule for each currency is not adequate because assume for example the price of Bitcoin 10x's, the formatting rule will need updating. I'm looking for a solution that doesn't need any manual maintainance/checking.

like image 878
Tom Gullen Avatar asked May 22 '14 17:05

Tom Gullen


2 Answers

For a given decimal value X, you want to find the smallest integer Y such that YA + B as close as possible to X, for some given A and B. E.g. in the case of dollar, you have A = .5 and B = .49.

In general, for your problem, A and B can be computed via the formula:

V = value of £0.10 in target currency
K = smallest power of ten (10^k) such that 9*10^k >= V
    and k <= -2 (this condition I added based on your examples, but contrary
                 to your definition)
  = 10^min(-2, ceil(log10(V / 9))) 
A = 50 * K
B = 49 * K

Note that without the extra condition, since 0.09 dollars is less than 0.10 pounds, we would get 14.9 as the result for 16.86 dollars.

With some transformation we get

Y ~ (X - B) / A

And since Y is integer, we have

Y = round((X - B) / A) 

The result is then YA + B.

like image 156
Niklas B. Avatar answered Sep 28 '22 04:09

Niklas B.


  • Convert £0.10 to the current currency to determine the smallest displayable digit (SDD)
    (bounded by the number of available digits in that currency).

  • Now we basically have 3 choices of numbers:

    • ... (3rdSDD-1) 9 9 (if 3rdSDD is 0, it will obviously carry from 4thSDD and so on, as subtraction normally works)

      We'll pick this when 10*2ndSDD + 1stSDD < 24

    • ... 3rdSDD 4 9

      We'll pick this when 24 <= 10*2ndSDD + 1stSDD < 74

    • ... 3rdSDD 9 9

      We'll pick this when 74 < 10*2ndSDD + 1stSDD

  • It should be trivial to figure it out from here.
    Some multiplication and modulus to get you 2ndSDD and 1stSDD.
    Basic subtraction to get you ... (3rdSDD-1).
    A few if-statements to pick one of the above cases.

Example:

For $16.86, our 3 choices are $15.99, $16.49 and $16.99.
We pick $16.99 since 74 < 86.

For €12.33, our 3 choices are €11.99, €12.49 and €12.99.
We pick €12.49 since 24 <= 33 < 74.

For ¥1755, our 3 choices are ¥1699, ¥1749 and ¥1799.
We pick ¥1749 since 24 <= 55 < 74.

For ฿0.03482570, our 3 choices are ฿0.0299, ฿0.0349 and ฿0.0399.
We pick ฿0.0349 since 24 <= 48 < 74.

And, just to show the carry:

For $100000.23, our 3 choices are $99999.99, $100000.49 and $100000.99.
We pick $99999.99 since 23 < 24.

like image 32
Bernhard Barker Avatar answered Sep 28 '22 06:09

Bernhard Barker