Javascript number comparison not correct - javascript

I'm doing some maths in javascript but I'm not getting the expected result all the time.
Here's my function - some parts have been simplified.
function updateExample($widget) {
var loan = parseInt($widget.attr("data-loan"), 10);
var term = parseInt($widget.attr("data-term"), 10);
// Get the best rate
var rateInfo = GetRateInfo(loan, term);
var annualRate = rateInfo[2];
// Calculate costs
var rate = (term === 1
? annualRate / 365 * 30
: annualRate / 12) / 100;
var pow = Math.pow(rate + 1, term);
var payment = round(rate * pow / (pow - 1) * loan, 2);
var totalRepayable = round(payment * term, 2);
var totalCostCap = round(loan * 2, 2);
var costCapped = false;
console.log(totalRepayable);
console.log(totalCostCap);
if (totalRepayable > totalCostCap) {
console.log("capped");
}
}
One of the tests that's failing is when I pass in a loan of 500 and a term of 1.
As you can see, I log 3 values to the console. The first 2 values output are:
620.00 and 1000.00
Given the values, I expect the following test to fail but it doesn't.
if (totalRepayable > totalCostCap)
if (620.00 > 1000.00)
The console log reads "capped" to prove the if statement has been entered.
I'm not a javascript expert by any means but I can't see how this is failing.
Here's the custom round function:
function round(value, decimals) {
return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals).toFixed(decimals);
}
Any advice appreciated.

You don't show your round function, but I'm assuming it's using .toFixed(). The problem is you don't actually have arbitrary precision floating point numbers, so it converts to string, and
console.log("620.00" > "1000.00"); // true
The thing that tipped me off is that if you log a number like 620.00 to the console it automatically truncates it, the fact that you were seeing trailing zeros suggests it's a string.
Update
Yeah, now that you posted that it's definitely returning a string. The last part of the return value is a call to .toFixed(). Just cast the result back to a number to do the comparison.

Related

Need to convert number to currency

Currently using below code for conversion of number to currency. The only issue is if I have 1000 it is giving 1000 instead I need 1k.
Current implementation 1000 - 1000
Need 1000 - 1k
Tried in lot many ways to get it done but unable to resolve.
var number = 12345678910;
var digits = 2;
var suffix = ["", "K.", "M.", "B."];
var nbDigits = parseInt(Math.log(number)/Math.LN10);
var power = nbDigits - nbDigits%3;
var tmp = number/ Math.pow(10, power);
var suffixIndex = Math.min(3, power/3);
var result = "$" + tmp.toFixed(digits) + " " + suffix[suffixIndex];
I got this solution from this link
Just simplify calculation of number of digits:
// From:
var nbDigits = parseInt(Math.log(number)/Math.LN10);
// To:
var nbDigits1 = Math.log10(number);
That'll give you the number of digits, without rounding errors. It does return $1.00 K. for 1000.
Hope this helps!

Multiplication gives approximate results

Hmm I have an issue with roundings on the client side which is then validated in the backend and the validation is failing due to this issue. Here is the previous question Javascript and C# rounding hell
So what I am doing is:
On client side:
I have 2 numbers: 50 and 2.3659
I multiply them: 50 * 2.3659 //118.29499999999999
Round to 2 decimal places: kendo.toString(50 * 2.3659, 'n2') //118.29
In backend(C#):
I am doing the same: 50 and 2.3659
I multiply them: 50 * 2.3659 //118.2950
Round to 2 decimal places: Math.Round(50 * 2.3659, 2) //118.30
And validation is failing. Can I do something on the client side?
Can you try parseFloat and toFixed functions as follows :
var mulVal = parseFloat(50) * parseFloat(2.3659);
var ans = mulVal.toFixed(2);
console.log(ans);
Javascript Arithmetic is not always accurate, and such erroneous answers are not unusual. I would suggest that you use Math.Round() or var.toFixed(1) for this scenario.
Using Math.Round:
var value = parseFloat(50) * parseFloat(2.3659);
var rounded = Math.round(value);
console.log(rounded);
Prints 118 to the console.
Using toFixed() method:
var value = parseFloat(50) * parseFloat(2.3659);
var rounded = value.toFixed(1);
console.log(rounded);
Prints 118.3 to the console.
Note that using toFixed(2) will give the value as 118.29.
Hope this helps!
Haven't tested this extensively, but the function below should emulate the 'MidPointToEven' rounding:
function roundMidPointToEven(d, f){
f = Math.pow(10, f || 0); // f = decimals, use 0 as default
let val = d * f, r = Math.round(val);
if(r & 1 == 1 && Math.sign(r) * (Math.round(val * 10) % 10) === 5)
r += val > r ? 1 : -1; //only if the rounded value is odd and the next rounded decimal would be 5: alter the outcome to the nearest even number
return r / f;
}
for(let d of [50 * 2.3659, 2.155,2.145, -2.155, 2.144444, 2.1, 2.5])
console.log(d, ' -> ', roundMidPointToEven(d, 2)); //test values correspond with outcome of rounding decimals in C#

Why can't I use a variable created using prompt() within a function?

Below is a function which should produce a random number between two numbers which are specified by the user. The equation works if I manually specify the numbers. But if I use the prompt function it seems to produce a completely random number.
function randOm() {
var high = prompt("high");
var low = prompt("low");
return Math.floor(Math.random() * (high - low + 1)) + low;
}
document.write(randOm());
You will need to use parseFloat to convert it to a Number.
Convert the results of the prompt in numbers, because it returns strings:
return Math.floor(Math.random() * ((+high) - (+low) + 1)) + (+low);
prompt is known to return string, hence u have to convert the string to integer before performing your operations.And another disadvantage is that, if the user enters any characters like "hello" or "hi" in the prompt box, your function might return NaN, because cannot parse characters to numbers.
Script:
function randOm() {
var high = prompt("high");
var low = prompt("low");
var h=parseInt(high);
var l=parseInt(low);
return Math.floor(Math.random() * (h - l + 1)) + l;
}
document.write(randOm());
if (Number.isNaN(high) || Number.isNaN(low)){
alert ("both entries must be numbers!");
}
else{
low = parseFloat(low);
high = parseFloat(high);
return Math.floor(Math.random() * (high - low + 1)) + low;
}

Prompting for a variable not working when trying to find random number

My assignment for Intro to Javascript is to: "Write a function that accepts two numbers and returns a random number between the two values." It seems easy enough until I attempt to prompt the variables to input, at which point the output seems incorrect.
This is code that is most recommended for finding a random number between two ints, in this case 1 and 6:
function getRandomizer(bottom, top) {
return function() {
return Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom;
}
}
var rolldie = getRandomizer(1, 6);
document.write(rolldie());
The output I get is what it should be, a random integer between 1 and 6. However my assignment is to prompt for the numbers, which can be anything. So I do this, using 10 and 1 as example numbers:
var max = prompt("input 10");
var min = prompt("input 1");
function getRandomizer(bottom, top) {
return function() {
return Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom;
}
}
var rolldie = getRandomizer(min, max);
document.write(rolldie());
The output: 961. Second try: 231. etc. If I set the variables max and min directly to 10 and 1, the code works perfectly, returning numbers between 1 and 10. But for some reason, prompting input and then entering the exact same numbers gives completely different output. Why does this happen and how can I fix it?
The reason this happens is that the prompts are being treated as strings. So you're actually getting numbers between 1101 and 1.
You can ensure the vars min and max are numbers by using parseInt:
var max = parseInt(prompt("input 10"));
Try this Code below:
var max = Number(prompt("input 10"));
var min = Number(prompt("input 1"));

JavaScript math, round to two decimal places [duplicate]

This question already has answers here:
How to round to at most 2 decimal places, if necessary
(91 answers)
Closed 5 years ago.
I have the following JavaScript syntax:
var discount = Math.round(100 - (price / listprice) * 100);
This rounds up to the whole number. How can I return the result with two decimal places?
NOTE - See Edit 4 if 3 digit precision is important
var discount = (price / listprice).toFixed(2);
toFixed will round up or down for you depending on the values beyond 2 decimals.
Example: http://jsfiddle.net/calder12/tv9HY/
Documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed
Edit - As mentioned by others this converts the result to a string. To avoid this:
var discount = +((price / listprice).toFixed(2));
Edit 2- As also mentioned in the comments this function fails in some precision, in the case of 1.005 for example it will return 1.00 instead of 1.01. If accuracy to this degree is important I've found this answer: https://stackoverflow.com/a/32605063/1726511 Which seems to work well with all the tests I've tried.
There is one minor modification required though, the function in the answer linked above returns whole numbers when it rounds to one, so for example 99.004 will return 99 instead of 99.00 which isn't ideal for displaying prices.
Edit 3 - Seems having the toFixed on the actual return was STILL screwing up some numbers, this final edit appears to work. Geez so many reworks!
var discount = roundTo((price / listprice), 2);
function roundTo(n, digits) {
if (digits === undefined) {
digits = 0;
}
var multiplicator = Math.pow(10, digits);
n = parseFloat((n * multiplicator).toFixed(11));
var test =(Math.round(n) / multiplicator);
return +(test.toFixed(digits));
}
See Fiddle example here: https://jsfiddle.net/calder12/3Lbhfy5s/
Edit 4 - You guys are killing me. Edit 3 fails on negative numbers, without digging into why it's just easier to deal with turning a negative number positive before doing the rounding, then turning it back before returning the result.
function roundTo(n, digits) {
var negative = false;
if (digits === undefined) {
digits = 0;
}
if (n < 0) {
negative = true;
n = n * -1;
}
var multiplicator = Math.pow(10, digits);
n = parseFloat((n * multiplicator).toFixed(11));
n = (Math.round(n) / multiplicator).toFixed(digits);
if (negative) {
n = (n * -1).toFixed(digits);
}
return n;
}
Fiddle: https://jsfiddle.net/3Lbhfy5s/79/
If you use a unary plus to convert a string to a number as documented on MDN.
For example:+discount.toFixed(2)
The functions Math.round() and .toFixed() is meant to round to the nearest integer. You'll get incorrect results when dealing with decimals and using the "multiply and divide" method for Math.round() or parameter for .toFixed(). For example, if you try to round 1.005 using Math.round(1.005 * 100) / 100 then you'll get the result of 1, and 1.00 using .toFixed(2) instead of getting the correct answer of 1.01.
You can use following to solve this issue:
Number(Math.round(100 - (price / listprice) * 100 + 'e2') + 'e-2');
Add .toFixed(2) to get the two decimal places you wanted.
Number(Math.round(100 - (price / listprice) * 100 + 'e2') + 'e-2').toFixed(2);
You could make a function that will handle the rounding for you:
function round(value, decimals) {
return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
}
Example:
https://jsfiddle.net/k5tpq3pd/36/
Alternative
You can add a round function to Number using prototype. I would not suggest adding .toFixed() here as it would return a string instead of number.
Number.prototype.round = function(decimals) {
return Number((Math.round(this + "e" + decimals) + "e-" + decimals));
}
and use it like this:
var numberToRound = 100 - (price / listprice) * 100;
numberToRound.round(2);
numberToRound.round(2).toFixed(2); //Converts it to string with two decimals
Example
https://jsfiddle.net/k5tpq3pd/35/
Source: http://www.jacklmoore.com/notes/rounding-in-javascript/
To get the result with two decimals, you can do like this :
var discount = Math.round((100 - (price / listprice) * 100) * 100) / 100;
The value to be rounded is multiplied by 100 to keep the first two digits, then we divide by 100 to get the actual result.
The best and simple solution I found is
function round(value, decimals) {
return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}
round(1.005, 2); // 1.01
try using discount.toFixed(2);
I think the best way I've seen it done is multiplying by 10 to the power of the number of digits, then doing a Math.round, then finally dividing by 10 to the power of digits. Here is a simple function I use in typescript:
function roundToXDigits(value: number, digits: number) {
value = value * Math.pow(10, digits);
value = Math.round(value);
value = value / Math.pow(10, digits);
return value;
}
Or plain javascript:
function roundToXDigits(value, digits) {
if(!digits){
digits = 2;
}
value = value * Math.pow(10, digits);
value = Math.round(value);
value = value / Math.pow(10, digits);
return value;
}
A small variation on the accepted answer.
toFixed(2) returns a string, and you will always get two decimal places. These might be zeros. If you would like to suppress final zero(s), simply do this:
var discount = + ((price / listprice).toFixed(2));
Edited:
I've just discovered what seems to be a bug in Firefox 35.0.1, which means that the above may give NaN with some values.
I've changed my code to
var discount = Math.round(price / listprice * 100) / 100;
This gives a number with up to two decimal places. If you wanted three, you would multiply and divide by 1000, and so on.
The OP wants two decimal places always, but if toFixed() is broken in Firefox it needs fixing first.
See https://bugzilla.mozilla.org/show_bug.cgi?id=1134388
Fastest Way - faster than toFixed():
TWO DECIMALS
x = .123456
result = Math.round(x * 100) / 100 // result .12
THREE DECIMALS
x = .123456
result = Math.round(x * 1000) / 1000 // result .123
function round(num,dec)
{
num = Math.round(num+'e'+dec)
return Number(num+'e-'+dec)
}
//Round to a decimal of your choosing:
round(1.3453,2)
Here is a working example
var value=200.2365455;
result=Math.round(value*100)/100 //result will be 200.24
To handle rounding to any number of decimal places, a function with 2 lines of code will suffice for most needs. Here's some sample code to play with.
var testNum = 134.9567654;
var decPl = 2;
var testRes = roundDec(testNum,decPl);
alert (testNum + ' rounded to ' + decPl + ' decimal places is ' + testRes);
function roundDec(nbr,dec_places){
var mult = Math.pow(10,dec_places);
return Math.round(nbr * mult) / mult;
}

Categories