I'm trying to create a basic profit calculator but I am struggling with one issue.
I've written some basic javascript and the formula almost works. However my issue is that the decimal point doesn't seem to want to work properly. For example:
What is the case cost: 2.80
How may units per case: 2
What is the sell price: 3.15
Total Profit = 1.75 Profit should of course be, 0.175
I'm a complete newbie to JavaScript so your help would be much appreciated.
<form id="profitCalculator">
<p><label>What is the case cost? <input type="text" name="casecost"></label></p>
<p><label>How many packs / units per case? <input type="text" name="packs"></label></p>
<p><label>What is the sell price? <input type="text" name="sell_price"></label></p>
<p>Total profit £: <input type="text" name="profit"></p>
document.getElementById('profitCalculator').onclick = function () {
var casecost = this.elements['casecost'].value || 0;
var packs = this.elements['packs'].value || 0;
var sell_price = this.elements['sell_price'].value || 0;
var profit = sell_price - casecost / packs;
this.elements['profit'].value = profit.toFixed(2); }
Thanks
It should be
var profit = (sell_price - casecost) / packs;
BUT - Never calculate currency with decimals in Javascript!
Javascript will truncate decimal values when they become to long, possibly resulting in nasty rounding errors. Always multiply your values by 100, then calculate everything, and at last, divide by 100 again.
Look at order of operations, you may know this as 'BODMAS'
Supporting Link: http://www.mathsisfun.com/operation-order-bodmas.html
Change to (sell_price - casecost) / packs;
your problem occurs because operators procedence.
var profit = sell_price - casecost / packs;
/ (division) occurs first than - (minus).
As your example.
2.80 / 2 = 1.4
3.15 - 1.4 = 1.75
You should put some parenthesis covering what has to priority, in your case, to get the value 0.175, you should put the like this.
(3.15 - 2.80) / 2 = 0.175
in code
var profit = (sell_price - casecost) / packs;
See MDN's reference on Operator Precedence and you'll see that division (and multiplication) is done before addition or subtraction. So you have essentially:
3.15 - (2.80 / 2) = 1.75
Instead of:
(3.15 - 2.80) / 2 = 0.175
Also note, as #Adrian Schmidt pointed out, using floating point numbers for math is a bad idea. If you do that above calculation in javascript you actually get:
0.17500000000000004
Because computers don't have infinite precision when representing floating point numbers. See, for example: Is floating point math broken?
So your formula should be:
(sell_price - casecost) / packs
Another thing to consider is that the values you get from your text boxes are strings, not numbers. Your formula works because there is no - operator for strings, so javascript automatically converts your values to numbers. But this is a dangerous thing to rely on. For example, if you did this:
sell_price + casecost
With your example inputs, the result would be:
"3.152.80"
Because it's doing string concatenation, not addition.
So it's worth using parseFloat to convert your strings. (and parseInt for packs as it is, presumably, an integer)
So a complete example might look like this:
var casecost = parseFloat(this.elements['casecost'].value) * 100 || 0;
var packs = parseInt(this.elements['packs'].value, 10) || 0;
var sell_price = parseFloat(this.elements['sell_price'].value) * 100 || 0;
var profit = ((sell_price - casecost) / packs) / 100;
this.elements['profit'].value = profit.toFixed(2);
Also note that if packs is 0, then you'll have a divide by zero error. You'll want to add logic to check the value of packs and do something when it's zero (not calculate the profit).
Related
For example, I have a number 123.429. How can I remove the trailing decimals without rounding up to two decimal place.
Hence, I need the number to be up to two d.p. i.e 123.42.
Definitely toFixed() method or Math.round(num * 100) / 100 cannot be used in this situation.
The function you want is Math.floor(x) to remove decimals without rounding up (so floor(4.9) = 4).
var number = Math.floor(num * 100) / 100;
Edit: I want to update my answer because actually, this rounds down with negative numbers:
var Math.floor(-1.456 * 100) / 100;
-1.46
However, since Javascript 6, they have introduced the Math.trunc() function which truncates to an int without rounding, as expected. You can use it the same way as my proposed usage of Math.floor():
var number = Math.trunc(num * 100) / 100;
Alternatively, the parseInt() method proposed by awe works as well, although requires a string allocation.
var number = parseInt('' + (num * 100)) / 100;
You can convert it to a string and then simply truncate the string two places after the decimal, e.g.:
var s = String(123.429);
s.substring(0, s.indexOf('.') + 3); // "123.42"
Please note that there's no guarantee if you convert that final string back into a number that it'll be exactly representable to those two decimal places - computer floating point math doesn't work that way.
another v. cool solution is by using | operator
let num = 123.429 | 0
let num = 123.429 | 0
console.log(num);
let's get the variable name as "num"
var num = 123.429;
num=num*100;
num=num.toString();
num=num.split(".");
num=parseInt(num[0]);
num=num/100;
value of the num variable will be 12.42
Try this
number = parseFloat(number).toFixed(12);
number = number.substring(0, number.indexOf('.') + 3);
return parseFloat(number);
Not the fastest solution but the only one that handles an edge case like 0.0006*10000 = 5.999999999 properly, i.e. if you want to truncate to 4 decimal places and the value is exactly 0.0006, then using Math.trunc(0.0006 * (10 ** 4))/(10 ** 4) gives you 0.0005.
I've following formula in Wolfram:
log base 10 of (10^-18x)
WolframLink (with x example)
First of all Log Function:
function getLog(y) {
return Math.log(y)/Math.log(10);
}
Now I'm trying to get my value:
var x = Math.pow(10,33);
var faktor = getLog(Math.pow(10,-(18*x)));
console.log(faktor);
console gives -Infinity
What is wrong about my code? Thanks for help.
Your computations are causing floating point underflow. A floating point number is roughly represented as 0.<mantissa> × 2<exponent>, so that the distribution of log(|x|) for representable values x is uniformly dense. Both the mantissa and exponent have a certain number of bits allocated for them. The exponent of the representation can only become so small, and if you try to make it smaller than the minimum, the representation is forced to round to 0.
You can instead perform your computations in logspace using these identities. A few of these are listed here.
log(x × y) = log(x) + log(y)
log(x / y) = log(x) - log(y)
log(xy) = y × log(x)
You are overflowing the maximum integer allowed in JavaScript by quite a bit.
Max int in JS
Wolfram proof because why not
You are going to need a big number extension of JavaScript, which SO has many posts about. See here for what appears to be unlimited integer math
More posts like this can be chased starting here
Code
Values in practice:
var x = Math.pow(10,33); // x = 1.0000000000000001e+33
var exponent = -18*x; // exponent = -1.8000000000000002e+34
var deci = Math.pow(10, exponent); // deci = 0
var logged = Math.log(deci); // logged = NaN (-Infinity)
var result = logged/Math.log(10); // result = NaN (-Infinity)
The problem is that floating point values can have decimal exponents in the range -308 to +308 (approximately, it's really all in binary), and your exponent is approximately 18,000,000,000,000,002,000,000,000,000,000,000.
Maths
Maths gives us log(xy) = y.log(x). And that logb(b) = 1. Thus,
log10(10-18x) = -18x.log(10) = -18x.
So the maths lesson is that "logging X (base b)" and "raising b to the power of X" are inverse operations.
FYI: random == pseudo-random
A. when generating uniformly-random numbers, I can specify a range, i.e.:
(Math.random()-Math.random())*10+5
//generates numbers between -5 and 15
B. generating a set of random values with a version of Gaussian-esque normal randomness:
//pass in the mean and standard deviation
function randomNorm(mean, stdev) {
return Math.round((Math.random()*2-1)+(Math.random()*2-1)+(Math.random()*2-1))*stdev+mean);
}
//using the following values:
{
mean:400,
standard_deviation:1
//results in a range of 397-403, or +-range of 3
},
{
mean:400,
standard_deviation:10
//results in a range of 372-429, or +-range of 30
},
{
mean:400,
standard_deviation:25
//results in a range of 326-471, or +-range of 75
}
each one gives me a range of approximately standard_deviation*(+-3) (assuming I left the program running longer).
C. I can calculate this range as follows:
assuming I want a range from 300-500, so var total_range = 200;
my mean is 400, my +-range is total_range/2 (var r = 100)
so standard_deviation would be r/3 or in this case 33.333.
This seems to be working, but I have no idea what I'm doing with math so I feel like an idiot, this solution feels kludgy and not totally accurate.
My question:
is there some formula that I'm dancing around that can help me here? my requirements are as follows:
must be able to define a range of numbers accurately.
must be done in JavaScript, as efficiently as possible.
I think maybe I'm close but it's not quite there.
Subtracting two random numbers doesn't give you a normal distribution, it will give you numbers that decline linearly on both sides of zero. See the red diagram in this fiddle:
http://jsfiddle.net/Guffa/tvt5K/
To get a good approximation of normal distribution, add six random numbers together. See the green diagram in the fiddle.
So, to get normally distributed random numbers, use:
((Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random()) - 3) / 3
This method is based on the central limit theorem, outlined as the second method here: http://en.wikipedia.org/wiki/Normal_distribution#Generating_values_from_normal_distribution
I wanted to have gaussian random numbers between 0 and 1, and after many tests (thanks to #Guffa answer too) I found this to be the best:
function gaussianRand() {
var rand = 0;
for (var i = 0; i < 6; i += 1) {
rand += Math.random();
}
return rand / 6;
}
And as a bonus:
function gaussianRandom(start, end) {
return Math.floor(start + gaussianRand() * (end - start + 1));
}
I have a javascript function that converts feet into a usable object that will take the total feet and break it down into feet / inch / fraction.
In the example below the decimal fraction portion should be zero and my inch should be 4
Lets say I've got the following.
var inches = 40;
Now when I call the function below like so unPackDecimalFeet(40 /12);
unPackDecimalFeet: function (feetInchFraction) {
var inchFraction = (feetInchFraction - ~~feetInchFraction) * 12;
return {
feet: ~~feetInchFraction,
inch: ~~inchFraction,
fraction: inchFraction - ~~inchFraction
};
}
My return value is below.
feet: 3
fraction: 0.9999999999999964
inch: 3
**The above return value should read.
feet: 3
fraction: 0
inch: 4
How would I need to process the feet so that I can get the correct return value?
I'd say multiply it by 1000 in the beginning, then do your calculations, and then divide by 1000. IEEE Floating Point arithmetic is the problem here. If you need more calculations, look into BigInteger libraries. (BigInt, sometimes)
var inchesIn;
inchesIn = 40;
document.writeln('Feet: ' + Math.floor(inchesIn/12));
document.writeln('Inches: ' + inchesIn%12);
You're doing a lot to try and save yourself from tiny little inconsequential fraction issues throughout your code. I think maybe simpler is better:
unPackDecimalFeet : (function (feet) {
var flooredFeet = 0 | feet,
flooredInches = 0 | ((feet - flooredFeet) * 12),
inchFraction = (feet * 12) - (0 | (feet * 12));
return {
feet: flooredFeet,
inch: flooredInches,
fraction: +(inchFraction.toFixed(2)) //Rounded to two decimals
};
})
The only "trickiness" there is with 0 | as an alternative to Math.floor(). Feel free to use Math.floor() if it makes you feel better about maintainability. Also, I rounded off the "fraction" part of the response to the nearest 100th of an inch. You can round more precisely if you want, but some rounding is encouraged to prevent things like 0.9999999999999994 or whatever due to IEEE-754 gone wild.
In php, we have number_format(). Passing it a value such as:
number_format(3.00 * 0.175, 2);
returns 0.53, which is what I would expect.
However, in JavaScript using toFixed()
var num = 3.00 * 0.175;
num.toFixed(2);
returns 0.52.
Ok, so perhaps toFixed is not what I want... Maybe something like this...
var num = 3.17 * 0.175;
var dec = 2;
Math.round( Math.round( num * Math.pow( 10, dec + 1 ) ) / Math.pow( 10, 1 ) ) / Math.pow(10,dec);
No, that doesn't work either. It will return 0.56.
How can I get a number_format function in JavaScript that doesn't give an incorrect answer?
Actually I did find an implementation of number_format for js, http://phpjs.org/functions/number_format, but it suffers from the same problem.
What is going on here with JavaScript rounding up? What am I missing?
JavaScript does badly with floating point numbers (as do many other languages).
When I run
3.000 * 0.175
In my browser, I get
0.5249999999999999
Which will not round up to 0.525 with Math.round. To circumvent this, you kind of have to multiply both sides until you get them to be integers (relatively easy, knowing some tricks help though).
So to do this we can say something like this:
function money_multiply (a, b) {
var log_10 = function (c) { return Math.log(c) / Math.log(10); },
ten_e = function (d) { return Math.pow(10, d); },
pow_10 = -Math.floor(Math.min(log_10(a), log_10(b))) + 1;
return ((a * ten_e(pow_10)) * (b * ten_e(pow_10))) / ten_e(pow_10 * 2);
}
This may look kind of funky, but here's some pseudo-code:
get the lowest power of 10 of the arguments (with log(base 10))
add 1 to make positive powers of ten (covert to integers)
multiply
divide by conversion factor (to get original quantities)
Hope this is what you are looking for. Here's a sample run:
3.000 * 0.175
0.5249999999999999
money_multiply(3.000, 0.175);
0.525
The toFixed function is working correctly. It truncates past the specified amount of fraction digits.
Why all the powers?? Why not just add slightly less than 1/2 a cent and round:
(3.00 * 0.175 + 0.0049).toFixed(2)
Never had any accountants complain about the output.
I think the problem you are encountering is with floating point math as opposed to the rounding itself.
Using the firebug console for testing, logging the result of 3.00 * 0.175 given 0.524999.... So rounding this number down is actually correct.
I don't know if there is a good solution to your problem, but in my experience when working with currency: it is easier to work in the smallest unit (cents) and then convert for display.
Why didn't you just use Math.round( num * Math.pow( 10, dec ) ) / Math.pow( 10, dec) )?
EDIT: I see, the problem is that 3 * 0.175 gives you 0.52499999999999991, leading you to want an additional rounding step. Maybe just adding a small amount would work:
Math.round( num * Math.pow( 10, dec ) + 0.000000001 ) / Math.pow( 10, dec) )
I know this is old but this is how I usually solve a rounding problem. This can be put in a function easily but for now I just put in simple vars for right now. If this doesn't work you could use money_format() or number_format() as a start from php.js (more info below).
var n = (3.00 * 0.175);
n = Math.round(n * Math.pow(10, 3)) / Math.pow(10, 3);
Math.round(n*100)/100;
comes out to 0.53 (0.5249999999999999)
var n = (3.00 * 0.175);
n = Math.round(n * Math.pow(10, 3)) / Math.pow(10, 3);
Math.round(n*100)/100;
comes out to 0.56 (0.55475)
It also looks like the php.js repo is being kept up on GitHub https://github.com/kvz/phpjs so if there isn't a function that is not performing correctly an issue can be submitted.
Anyway figured this information may help someone looking later on.