Custom Rounding up to nearest 0.05 - javascript

I want to do custom round up to near 0.05 , based on following condition.It is hard to explain , but following example will be easy to understand .
12.910 - 12.90
12.920 - 12.90
12.930 - 12.90
12.940 - 12.90
12.941 - 12.95
12.950 - 12.95
12.960 - 12.95
12.970 - 12.95
12.980 - 12.95
12.990 - 12.95
12.991 - 13.00
13.000 - 13.00
I tried several function , but it is rounding up 12.98 to 13.00.
function customRound( num) {
return Math.round(num * 20) / 20;
}

Coming at this visually, your rounding algorithm seems to look like this:
The dot is where you want to round to for that interval. ( marks the open end of an interval, ] the closed end. (12.99 belongs to the red interval.) We'll implement this algorithm by manipulating the line to match Math.floor's.
First, let's work with integers.
num * 100
Your rounding interval is left-open and right-closed, but Math.floor is left-closed and right-open. We can flip the line to match by multiplying by −1:
num * 100 * -1
⇒ num * -100
Your rounding intervals' lengths are 5, so we need to put the ends of the intervals on multiples of 5...
num * -100 - 1
...before dividing by 5 to match Math.floor.
(num * -100 - 1 ) / 5
⇒ num * -20 - 0.2
Now we can take the floor.
return Math.floor(num * -20 - 0.2);
Scale back up to the original by multiplying by 5:
return Math.floor(num * -20 - 0.2) * 5;
Shift the returned value over to the dot by adding 4:
return Math.floor(num * -20 - 0.2) * 5 + 4;
Undo the alignment we did earlier:
return Math.floor(num * -20 - 0.2) * 5 + 4 + 1;
⇒ return Math.floor(num * -20 - 0.2) * 5 + 5;
Undo the flip:
return (Math.floor(num * -20 - 0.2) * 5 + 5) * -1;
⇒ return Math.floor(num * -20 - 0.2) * -5 - 5;
And divide the whole thing by 100 to get your original scale back:
return (Math.floor(num * -20 - 0.2) * -5 - 5) / 100;
⇒ return Math.floor(num * -20 - 0.2) * -0.05 - 0.05;
Using Robin Zigmond's testing framework,
function customRound(num) {
return Math.floor(num * -20 - 0.2) * -0.05 - 0.05;
}
// test desired results
var tests = [12.91, 12.92, 12.93, 12.94, 12.941, 12.95, 12.96, 12.97, 12.98, 12.99, 12.991, 13];
for (var i=0; i<tests.length; i++) {
console.log(`${tests[i].toFixed(3)} - ${customRound(tests[i]).toFixed(2)}`);
}

If you really intended to round everything within < .01 of the nearest .05 then try the below, to get the precision of the number it uses answer from Is there a reliable way in JavaScript to obtain the number of decimal places of an arbitrary number?
function decimalPlaces(n) {
var s = "" + (+n);
var match = /(?:\.(\d+))?(?:[eE]([+\-]?\d+))?$/.exec(s);
if (!match) { return 0; }
return Math.max(
0, // lower limit.
(match[1] == '0' ? 0 : (match[1] || '').length)
- (match[2] || 0));
}
var test = 12.941;
var factor = Math.pow(10,decimalPlaces(test));
var remainder = ((test * factor) % (.05 * factor))/factor;
var result;
if (remainder>.04) {
result = Math.round(test*20)/20;
} else {
result = (test*factor - remainder*factor)/factor;
}
console.log('result is:',result);

As far as I can tell from your example, the desired behaviour appears to be "round up to the nearest 0.01, then round that result down to the nearest 0.05".
This can be implemented as follows. As you can see, it agrees exactly with your examples (I even took care to format it the same way) - but please let me know if I've got the wrong end of the stick.
function customRound(num) {
var intermediateResult = Math.ceil(num*100)/100;
return Math.floor(intermediateResult*20)/20;
}
// test desired results
var tests = [12.91, 12.92, 12.93, 12.94, 12.941, 12.95, 12.96, 12.97, 12.98, 12.99, 12.991, 13];
for (var i=0; i<tests.length; i++) {
console.log(`${tests[i].toFixed(3)} - ${customRound(tests[i]).toFixed(2)}`);
}

Related

Represent a number with a color, difference not enough for percentage based rgb

I have an array (1200 values) of numbers
[123, 145, 158, 133...]
I'd like to have a div for each value with a background color from red to green, red being the smallest number and green the largest.
The base setup looks like this: (templating with vuejs but unrelated to the problem)
const values = [123, 145, 158, 133...]; // 1200 values inside
const total = values.length;
<div
v-for="(val, i) in values"
:key="i"
:style="{backgroundColor: `rgb(${(100 - (val*100/total)) * 256}, ${(val*100/total) * 256}, 0)`}">
{{val}}
</div>
I'm not a maths specialist but since all my numbers are around 100, the rgb generated is the same. (around 12% yellowish color)
How can I give more weight to the difference between 137 and 147?
EDIT: final formula:
:style="{backgroundColor: `rgb(${(256/(maxValue-minValue) * (boule-maxValue) - 255)}, ${(256/20 * (boule-maxValue) + 255)}, 0)`}"
Checkout this post: https://stats.stackexchange.com/questions/70801/how-to-normalize-data-to-0-1-range.
Basically you want to linearly rescale your values to another interval. You need your current min and max values from the array. Then define the new min' and max' which are the limits of the new interval. This would be [0, 255] in your case.
To do the transformation use the formula:
newvalue= (max'-min')/(max-min)*(value-max)+max'
As an example:
If your min value is 127 and max is 147, and you want to map 137. Then:
256/20 * (137-147) + 255 which results in 127.
If you want to map 130. Then:
256/20 * (130-147) + 255 = 37.4.
It really depends on what meaning those values actually have
However, you can try this: if your values are always bigger than 100 and always less than 150 (you can choose these number of course) you can "stretch" your values using the values as minimum and maximum. Let's take 137 and 147 as examples:
(val-min) : (max-min) = x : 255
(137-100):(150-100) = x:255 -> 37:50 = x:255 -> 188
(147-100):(150-100) = x:255 -> 47:50 = x:255 -> 239
That is for the math. In the end, this is the calculation:
newValue = (val-min)*255/(max-min)
where min and max are your chosen values.
You could take a kind of magnifier for a range of data. In this example, the values between 20 and 30 are mapped to a two times greater range than the outside values inside of an interval of 0 ... 100.
function magnifier(value, start, end, factor) {
var middle = (start + end) / 2,
size = (end - start) * factor / 2,
left = middle - size,
right = middle + size;
if (value <= start) return value * left / start;
if (value <= end) return (value - start) * factor + left;
return (value - end) * (100 - right) / (100 - end) + right;
}
var i;
for (i = 0; i <= 100; i += 5) {
console.log(i, magnifier(i, 20, 30, 2));
}
.as-console-wrapper { max-height: 100% !important; top: 0; }

Basic formula to calculate decimal between two numbers

I'm having a brain fart and need help with a basic formula.
if (d >= 0.1) {
scale = 0.1
} else if (d <= 0.004) {
scale = 1;
}
How can I set the value of scale to somewhere between 0.1 and 1 if the value of d is somewhere between 0.1 and 0.004?
You can add an else statement to handle the numbers between 0.004 and 0.1:
// ...
else {
scale = 1 - (d - 0.004) * (1 - 0.1) / (0.1 - 0.004);
}
Or you can do the whole thing in one go:
var scale = 1 - (Math.max(Math.min(d, 1), 0.004) - 0.004) * (1 - 0.1) / (0.1 - 0.004);
The scaling should look like this:
scale = 1 - (1 - 0.1)*(d - 0.004)/(0.1 - 0.004)
or more simply
scale = 1 - 9.375*(d - 0.004)
For example, if d = 0.05, then scale = 0.56875.
If I understand correctly from the comments, this is the pattern for your calculation. dRange is the range of d values. The input values are limited to also restrict the output.
Note: The input values d are first restricted, so that we have 2 absolute 'scales' that we can mirror: dRange and scaleRange, and their corresponding min- and max-values.
Note: The inverted scaleRange, where 1 (max value) is to the left, and 0.1 (min value) is to the right:
0.004 <-- dRange --> 0.1
~~~~~~~~~~~~|--------------------------------------------------------------|~~~~~~~~~~~~
In: ~~~d~~~~~d----------------------d---------------------------------------d-----d------
| | | | |
----->| | |<----
| | |
Limited: [d,d]--------------------d-------------------------------------[d,d]
| | |
Out: [x,x]--------------------x-------------------------------------[x,x]
~~~~~~~~~~~~|--------------------------------------------------------------|~~~~~~~~~~~~
1 <-- scaleRange --> 0.1
That's some of the more complex logic that deserves a slightly more declarative approach. I came up with the following to implement the scenario above. Further explanation is in the code comments:
// The minimum and maximum values for d - also the 'input'
const minDValue = 0.004;
const maxDValue = 0.1;
// The minimum and maximum values for scale - also the 'output'
const minScaleValue = 0.1;
const maxScaleValue = 1;
// Absolute ranges for d and scale:
const dRangeAbs = maxDValue - minDValue;
const scaleRangeAbs = maxScaleValue - minScaleValue;
// restricts the value of d if it exceeds the bounds:
// 'minDValue <= d <= maxDValue'
function restrictD(d) {
return Math.max(minDValue, Math.min(maxDValue, d));
}
// Calculate the absolute scale value for a certain d-value.
function scaleForDValue(d) {
// First, restrict the input. If not restricted the input 'scale' is
// not accurate, and we can not determine the corresponding scale.
d = restrictD(d);
// Determine how far (as fraction of 1) the d-value sits along
// the absolute d-range:
const dProgressFraction = (d - minDValue) / dRangeAbs;
// Use the d-progress-fraction to add a corresponding relative
// progress value for the scale-'scale'. Because the scale is
// inverted, we subtract the progress from the max-value this
// time.
const calculatedScale = maxScaleValue - (dProgressFraction * scaleRangeAbs);
// Restrict calculated value to prevent rouding error.
return Math.min(maxScaleValue, Math.max(minScaleValue, calculatedScale));
}
// Log some values!
const log = (val, info) => console.log({ d: val, scale: scaleForDValue(val), info });
log(minDValue, 'this is the min d-value');
log(minDValue - 1, 'this is a too low d-value');
log(maxDValue, 'this is the max d-value');
log(maxDValue + 1, 'this is a too high d-value');
for (let i = 0; i < 5; i++) {
log(minDValue + Math.random() * dRangeAbs, 'A random d-value');
}

How can I calculate my odds of x happening?

What is the percent change of this code reaching the number 200, 500 and 1000?
I created this code for 200 to be 50% but it keeps rolling numbers above 200, someone please help me if you understand :D.
var mainMultplier = 100;
var numerator = 99;
var denominator = 100;
for(;;) {
var randomInt = random.real(0, 1.0);
if ( numerator/denominator > randomInt ) {
numerator = numerator + 1;
denominator = denominator + 1;
mainMultplier = mainMultplier + 1;
} else {
break;
}
}
Edit
Based on the code you have posted, we can see these two base rules:
P(100) = 1 - 0.99 = 0.01
P(101) = (1 - P(100)) * (1 - (100 / 101)) = P(101) = (1 - P(100)) * (1 / 101)
The second rule can be generalized for any number X after 100:
P(X) = (1 - P(X - 1)) * (1 / X)
Now, I did learn how to do proofs by induction at Uni, which I'm sure would help my explanation here, but I can't remember it anymore :(. So instead, I've written some code to generate a lookup table p, from 100 to 1000:
var p = [];
p[100] = 0.01;
for (var x = 101; x <= 1000; x++)
p[x] = (1 - p[x - 1]) * (1 / x);
Edit 2
And that's as far as my help can go. You may want to post the generalized algorithm on the Software Engineering page.

How to generate random integers which are multiples of 30 in JavaScript

I'm trying to generate random integers that are are multiples of 30 in JavaScript.
That is:
0 60 0 180 120 ...... and so on
in range between 0 to 360 for example
So I am looking for a function something like this:
function (_range,_multi)
{
Math.round(...);
return rndNum;
}
Generate a random number between 0 and 12 (range) and multiply by 30 (multi):
Math.floor(Math.random() * 12) * 30
This gives you [0, 360) (so you never get 360)
Here's a live demo that shows a full working function - the general idea is that you multiply by (max/multiple), floor the value, then multiply it by the multiple:
function generate(min, max, multiple) {
var res = Math.floor(Math.random() * ((max - min) / multiple)) * multiple + min;
return res;
}
alert(generate(0, 360, 30));
Seems like that should work.
function randomMultiple(max, mult) {
return Math.floor(Math.random() * (max / mult)) * mult;
}
Thus a call of randomMultiple(360, 30) would produce an element of G with
G = { y = 30 * x | 0 < x < 12 }

round up nearest 0.10

I need to round up to the nearest 0.10 with a minimum of 2.80
var panel;
if (routeNodes.length > 0 && (panel = document.getElementById('distance')))
{
panel.innerHTML = (dist/1609.344).toFixed(2) + " miles = £" + (((dist/1609.344 - 1) * 1.20) + 2.80).toFixed(2);
}
any help would be appreciated
var number = 123.123;
Math.max( Math.round(number * 10) / 10, 2.8 ).toFixed(2);
If you need to round up, use Math.ceil:
Math.max( Math.ceil(number2 * 10) / 10, 2.8 )
Multiply by 10, then do your rounding, then divide by 10 again
(Math.round(12.362 * 10) / 10).toFixed(2)
Another option is:
Number(12.362.toFixed(1)).toFixed(2)
In your code:
var panel;
if (routeNodes.length > 0 && (panel = document.getElementById('distance')))
{
panel.innerHTML = Number((dist/1609.344).toFixed(1)).toFixed(2)
+ " miles = £"
+ Number((((dist/1609.344 - 1) * 1.20) + 2.80).toFixed(1)).toFixed(2);
}
To declare a minimum, use the Math.max function:
var a = 10.1, b = 2.2, c = 3.5;
alert(Math.max(a, 2.8)); // alerts 10.1 (a);
alert(Math.max(b, 2.8)); // alerts 2.8 because it is larger than b (2.2);
alert(Math.max(c, 2.8)); // alerts 3.5 (c);
This is a top hit on google for rounding in js. This answer pertains more to that general question, than this specific one. As a generalized rounding function you can inline:
const round = (num, grainularity) => Math.round(num / grainularity) * grainularity;
Test it out below:
const round = (num, grainularity) => Math.round(num / grainularity) * grainularity;
const test = (num, grain) => {
console.log(`Rounding to the nearest ${grain} for ${num} -> ${round(num, grain)}`);
}
test(1.5, 1);
test(1.5, 0.1);
test(1.5, 0.5);
test(1.7, 0.5);
test(1.9, 0.5);
test(-1.9, 0.5);
test(-1.2345, 0.214);
var miles = dist/1609.344
miles = Math.round(miles*10)/10;
miles = miles < 2.80 ? 2.80 : miles;
to round to nearest 0.10 you can multiply by 10, then round (using Math.round), then divide by 10
Round to the nearest tenth:
Math.max(x, 2.8).toFixed(1) + '0'
Round up:
Math.max(Math.ceil(x * 10) / 10, 2.8).toFixed(2)

Categories