I'm trying to calculate a series of values which should aggregate to the same as the Original Value.
If you look at the above example, you can see that the aggregated cost and billed figures are aggregating to 239.99 and 219.98 respectively. They obviously need to aggregate to 240 and 220 respectively.
The cost and billed figures are calculated when Hours changes (by using a jQuery on change function). The multiplier is determined by dividing the Original Value Hours by the number specified in the Calculated Values Hours below.
(0.2 / 0.7) = 0.28571428571428575
(0.3 / 0.7) = 0.4285714285714286
Self-evidently, the figures need to aggregate to the Original Values.
This is an issue no doubt to do with floating point precision and rounding and I've tried using the following function without success:-
function to2DP(num, fixed) {
var re = new RegExp('^-?\\d+(?:\.\\d{0,' + (fixed || -1) + '})?');
return num.toString().match(re)[0];
}
$("#splitTimeContainer").on("change", ".changeHours", function(e) {
var splitID = $("#splitID").val();
var splitOrigCost = $("#splitOrigCost").val();
var splitOrigBilled = $("#splitOrigBilled").val();
var splitOrigHours = $("#splitOrigHours").val();
var i = 1;
var divideMe = $(this).val();
var coEfficient = (divideMe/splitOrigHours);
var newCost = to2DP((coEfficient * splitOrigCost), 2);
var newBilled = to2DP((coEfficient * splitOrigBilled), 2);
var parent = $(this).parent().parent();
parent.find('.changeCost').val(newCost);
parent.find('.changeBilled').val(newBilled);
});
As well as using toFixed, but again without success.
Anyone have any suggestions at how I go about achieving this?
This question is more about rounding and mathematics.
I recommend to use different rounding to even. This rounding at least has no bias so it will not systematically increase or decrease values.
Another problem is that even with this rounding there is no guarantee that sum of values will match due to odd number of calculated values.
In this particular example this rounding will achieve cost 240 and billed 220.01.
This problem in my opinion has not optimal solution.
Consider this:
Hours: 0.9, Cost 100
hours: 0.3, cost 33.33
hours: 0.3, cost 33.33
hours: 0.3, cost 33.33
There is no way how to make this working.
You might want to consider adding the amount lost during division to one or more values.
Splitwise also does that to ensure no loss of value.
Related
I'm facing the next problem.
I am performing mathematical operations to calculate weights where I am using a function to return only 2 decimal places but sometimes the result is not as expected. Here is an example
I have a paper that weighs 49.8 and I want to subtract 1/4, that is 12.45 and do it 4 times but when I do this subtraction 49.8 - 12.45 the result gives me 37.34999999999999999999994 when in fact it should be 37.35 because if I do the whole process with my function the result would not be 0, it would be as follows
49.8 - 12.45 = 37.34
37.34 - 12.45 = 24.89
24.89 - 12.45 = 12.44
And you can't 12.44 - 12.45
So that's the problem because 49.8/4 = 12.45 but the way I'm doing it is not possible
How can I solve this, a way where if the third decimal place of the number is 9 round it and return it with only 2 decimals otherwise use my function to return it with 2 decimals
My function:
function threeDecimals(num) {
const multipliedNum = num * 100;
const truncatedNum = Math.trunc(multipliedNum);
const num3decimals = truncatedNum / 100;
return num3decimals;
}
Floating point numbers have this property by design. If you need to have base-10 precision, it's not enough to add a few decimals and round, because errors can still slip in in higher precisions.
You will need to use a third party library that guarantees base10 accuracy for the examples you gave.
Note that nothing will give you accuracy for every type of operation. Even Decimal.js will round 1/3 and not have a perfect representation, just like floating point numbers give seemingly 'incorrect' outcomes.
If you need exact calculations, operate on smaller units (milligrams instead of grams), so that intermediate values are always integers:
weight = 49800
f = (weight / 4) | 0
weight -= f
weight -= f
weight -= f
result = weight / 1000
console.log(result)
I have table like this:
The total of the Percentage column must be 100%, but I'm getting 100.01%.
Here's my code:
var hourSlow = $("#input-me-slow").val();
var slowTime = moment.duration(hourSlow).asHours();
// slow time return 2.1666666666666665
var hourIdle = $("#input-me-idle").val();
var idleTime = moment.duration(hourIdle).asHours()
// idle time return 1
var hourEco = $("#input-me-eco").val();
var ecoTime = moment.duration(hourEco).asHours();
// ecoTime return 20.166666666666668
var hourSpeed = $("#input-me-speed").val();
var speedTime = moment.duration(hourSpeed).asHours();
// speedTime return 0.6666666666666666
var fTime = "24:00"
var dfTime = moment.duration(fTime).asHours();
// dfTime return 24
var totalTime = dfTime-speedTime-ecoTime-slowTime-idleTime;
// totalTime return -2.220446049250313e-15
// Here for display it to table, the problem is here
var fPercent = toHour(fullTime); //return 2.78
var ePercent = toHour(ecoTime); //return 84.03
var sPercent = toHour(slowTime); //return 9.03
var iPercent = toHour(idleTime); //return 4.17
$("#me_fullpercen").text(addCommas(fPercent));
$("#me_ecopercen").text(addCommas(ePercent));
$("#me_slowpercen").text(addCommas(sPercent));
$("#me_idlepercen").text(addCommas(iPercent));
// here the function of toHour (I dont know maybe the problem is here)
function toHour(num) {
var result = (num / 24) * 100;
return result ;
}
I would rather not round the percentage to 100%, as that would be less precise.
How can I make my percentage 100% instead of 100.01%?
This is a common problem, and no matter how precise you try to be, the computer will need to round numbers with repeating decimals at some point. Here's some posts that deal with it:
How to make rounded percentages add up to 100%
How to deal with the sum of rounded percentage not being 100?
In those posts you can read about many complex ways to get very close to 100%, but basically there is no right way to do this - when it's all boiled down it's still going to be an estimate - not exactly precise because we're dealing with not-precise numbers. That's just the nature of the beast.
Your program is going to round numbers the wrong way because it's a computer, and it's not intelligent.
Depending on your application, you may want to invest time into reading how to do those complex methods, and maybe you'll get really close.
Add a Footnote
Any path you choose, you'll probably end up putting a footnote explaining this problem anyway. Something like this:
*Because of rounding, these values may not add up to 100%.
I have a text input for a money field.
I enter 33.91 into the field, and get the following results while trying to use the 'multiply by 100' technique.
var curWth = parseInt($('#trans_withdraw'+index).val()*100); // 3390
var curWth = parseInt($('#trans_withdraw'+index).val())*100; // 3300
var curWth = $('#trans_withdraw'+index).val()*100; // 3390.9999999...5
var curWth = parseFloat($('#trans_withdraw'+index).val())*100; // 3390.9999999...5
var curWth = parseFloat($('#trans_withdraw'+index).val()*100); // 3390.9999999...5
None of these give me 3391. I thought the whole idea of multiplying by 100 was to get away from the floating point problem. But as line 3 shows, when I multiply the value by 100 immediately, I still get floating point errors.
What am I still not understanding about working with currency?
I know 0.1 can't be stored correctly as a float. But multiplying by 100 is also not working for me in this case.
praseFloat to get your decimal value then multiple by 100 to get 3390.9999 then Math.round() to get the value.
var number = parseFloat($('#trans_withdraw').val()).toFixed(2);
var value = number * 100;
console.log(Math.round(value));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="trans_withdraw" value="33.91" />
I need to find the average of a set of values and after doing some reading am not sure if JavaScript is able to produce an accurate result or not.
Each value has a precision of 2 d.p. and there could be up to 10000 of them between -100000.00 and 100000.00. The result also needs to be to 2 d.p.
From what I can see it is usually the figures around the 16th decimal place that are inaccurate which means that I would have to average an extremely large set before affecting my result. Is the best way of doing it to simply sum all of the values, divide by the total number and then use a toFixed(2)?
You could take advantage of your 2dp prevision, and multiply all your numbers by 100 first, then do the mathematics using integers. EG, a float error occurs in this simple average (I am just using 1dp for this example):
(0.1 + 0.2) / 2
0.15000000000000002
But this works:
(0.1*10 + 0.2*10) / (2*10)
0.15
Some good reading here:
http://floating-point-gui.de/basic/
and here:
How to deal with floating point number precision in JavaScript?
and a really precise fix to do it using decimals is to use this:
https://github.com/dtrebbien/BigDecimal.js
Example for 2 dp:
var numbers = [0.10, 0.20, 0.30]
var simple_average = numbers.reduce(function(a, b) {
return a + b
}) / numbers.length
console.log(simple_average)
var smart_average = numbers.map(function(a) {
return a * 100
}).reduce(function(a, b) {
return a + b
}) / (numbers.length * 100)
console.log(smart_average)
This demo can be run here -- http://repl.it/e1B/1
I'm teaching myself JavaScript and have run into a problem with toFixed(). I'm working through an amortization calculator; and, one of the steps returns a number with a huge number of decimal places. I'm trying to cut it down to 4 decimal places.
Be advised the sample code has a lot of explanatory HTML in it. It's only there so that I can work through the steps of the equation. Also, when I add one to the very long number, it adds the numeral one to end of the scientific notation.
var paymentamount;
var principal=250000;
var interestrate = 4.5;
var annualrate = interestrate/12;
var numberofpayments = 360;
document.write("This is the annuitized interest rate: "+ annualrate +"%");
document.write("<h3> Now we add 1 to the annualized interest rate</h3>");
var RplusOne = annualrate + 1;
document.write("<p> This is One Added to R: " + RplusOne + "%");
document.write("<h3>Next RplusOne is Raised to the power of N </h3>");
var RRaised = (Math.pow(RplusOne, numberofpayments)).toFixed(4);
document.write("<p>This gives us the following very long number, even thought it shouldn't: " + RRaised);
document.write("<h3>Now we add one to the very long number </h3>");
var RplusOne = RRaised + 1;
document.write("<p>Now we've added one: " + RplusOne);
From MDN's documentation:
If number is greater than 1e+21, this method simply calls Number.prototype.toString() and returns a string in exponential notation.
The problem is that you are using 4.5 as your interest rate instead of 0.045, so doing this:
Math.pow(4.5 / 12 + 1, 360)
gives you a huge number (6.151362770461608e+49 or 6.15 * 10^49 to be exact). Change your interest rate to 0.045 and you will get what you are expecting.
As for the var RplusOne = RRaised + 1 line, the problem here is that RRaised is a string because of toFixed. I would only call toFixed when you're displaying things, and not at any other time; the primary reason for this would be to avoid rounding errors in subsequent calculations, but has the added benefit that your variables remain numbers and not strings.