TITLE: How to create a smooth percentage-based progression when there are large outliers (using only basic operations)
QUESTION [1 upvotes]: I have a series of amounts which people can donate to achieve various outcomes for a charitable project where one amount is usually much higher than the rest, for example (see also example image below):
15, 25, 70, 100, 6700
I am using a percentage-based width to highlight the background of each dollar amount to visually distinguish them and show their relative (to each other) impact. I'm also setting a minimum width to make sure a minimum amount is highlighted. It's hard to explain so please see the image for a better idea of what I'm trying to do.
I would like to create a smooth progression of the background color, but as you can see the outlier prevents this.
In this example, if 6700 is 100% of the largest option, how can I create more of a smooth progression for the smaller amounts?
IMPORTANT: I am limited to the following basic operations:

abs
ceil
divided_by
floor
minus
plus
round
times
modulo

Apologies if the title is confusing - maths is not my strong point and I wasn't sure how to accurately describe this - feel free to suggest alternative titles.

REPLY [1 votes]: This is a typical example where you could use logarithms (which are very good to smooth out situations with numbers of different magnitudes), but since you have no access to it, you can have a very rough approximation of it (the decimal places will be wrong but will still be good for what you want to do) by doing this (if you cannot use loops, please see the edit below):
Let's call the number you are working with n.

Count the number of digits of n. To do this, simply divide it by 10 until it is less than 10, count the number of steps and add 1. For example, take the number 856. Divide it by 10, the result is 85.6. Divide it again, and the result is 8.56 which is less than 10. Since we divided twice, it means the number has 2+1=3 digits.  
Take the power of ten that is immediately below n. To do this, start from 1, and multiply p times by 10, where p is the number found right before. For example, 856 had 3 digits, so start from 1, and multiply it 3 times by 10, which gives the result 1*10*10*10 = 1,000.
Finally, divide n by the result you just obtained, and add the first result you found. Here, it means you get the result 3 + 856/1,000, which is 3.856.

Algorithmically speaking:
input is n
n2 <- n
steps <- 0
while n2 > 10
    n2 <- n2 / 10
    steps <- steps + 1
steps <- steps + 1
steps2 <- steps
nextpower = 1
while steps2 > 0
    nextpower <- nextpower * 10
    steps2 <- steps2 - 1
return steps + n / nextpower

Here are a couple examples:
- 2 turns into 1.2
- 15 turns into 2.15
- 25 turns into 2.25
- 70 turns into 2.7
- 100 turns into 3
- 6,700 turns into 4.67
- 15,000,000 turns into 8.15
If you know that the numbers will stay in a certain range, from $n_{min}$ to $n_{max}$, you can compute the results $p_{min}$ and $p_{max}$ of the algorithm for these values. To obtain a percentage from any number $n$, all you have to do is compute $p$ using the algorithm above. The desired percentage is then $100*\frac{p-p_{min}}{p_{max}-{p_min}}$ (remove the $100$ if you want values between $0$ and $1$).
Note that if you have amounts that are smaller than 0.1, this will give you results that are below 0, so you might want to add 1 to the final result. If you have any amounts smaller than 0.01, add 2 to the final result, 3 for 0.001 and so on.
Edit: re-reading your post, it seems that you might not be allowed to use loops of any kind. Here is an algorithm using only the functions you can use; however, you will need to know the magnitude of the smallest and highest amount that can be present in the list. For example if your values are all between 1 and 999,999, you can use the following formula (it is awdully long and complicated, I know, but it is the best I can find in your situation):
(1*ceil(floor(N/1)/(floor(N/1)+1))
+10*ceil(floor(N/10)/(floor(N/10)+1))
+100*ceil(floor(N/100)/(floor(N/100)+1))
+1000*ceil(floor(N/1000)/(floor(N/1000)+1))
+10000*ceil(floor(N/10000)/(floor(N/10000)+1))
+100000*ceil(floor(N/100000)/(floor(N/100000)+1)))
+N/(1*ceil(floor(N/1)/(floor(N/1)+1))
+10*ceil(floor(N/10)/(floor(N/10)+1))
+100*ceil(floor(N/100)/(floor(N/100)+1))
+1000*ceil(floor(N/1000)/(floor(N/1000)+1))
+10000*ceil(floor(N/10000)/(floor(N/10000)+1))
+100000*ceil(floor(N/100000)/(floor(N/100000)+1)))/10

This will give you results that are reasonably close to the algorithm above, and you can then apply the same formula as earlier to get a percentage. You can also only keep the first part:
(1*ceil(floor(N/1)/(floor(N/1)+1))
+10*ceil(floor(N/10)/(floor(N/10)+1))
+100*ceil(floor(N/100)/(floor(N/100)+1))
+1000*ceil(floor(N/1000)/(floor(N/1000)+1))
+10000*ceil(floor(N/10000)/(floor(N/10000)+1))
+100000*ceil(floor(N/100000)/(floor(N/100000)+1)))

But this will give you the same result for numbers of the same magnitude (1 through 9 will give a 1, 10 through 99 will give a 2, 100 through 999 will give a 3 and so on).
If your values can go below 1, like earlier, you will need to add a line before the first one, replacing 1 with 0.1. If the values go below 0.1, you will need another line where you will replace 1 with 0.01, and so on.
{"set_name": "stack_exchange", "score": 1, "question_id": 1935020}