Javascript Math.floor issue between specific range of numbers - javascript

I'm facing an issue with Math.floor function of javascript for the below scenario:
1) from the value betwwen 8192 and 10484,
if I type 8192.8 -> The Math.floor converts it into 8192.79
if I type 8192.88 -> The Math.floor converts it into 8192.87
if I type 8192.3 -> The Math.floor converts it into 8192.29
Strange part is that except from the range given above the function works fine.
HTML:
<div data-bind="text: popIncrease"></div>
<input type="text" data-bind="value: userInput, valueUpdate: 'afterkeydown'" />
Javascript:
var ViewModel = function () {
var _self = this;
_self.userInput = ko.observable();
_self.popIncrease = ko.computed(function () {
return parseFloat((Math.floor(_self.userInput() * 100) / 100)).toFixed(2);
});
};
ko.applyBindings(new ViewModel());
jsfiddle:https://jsfiddle.net/91z5bdy4/1/
When I changed 100 with 1000 it solved the error but I do not understand why this happened on the first place?

You can just switch to this:
return parseFloat(_self.userInput()).toFixed(2);
Working version of your jsFiddle: https://jsfiddle.net/jfriend00/5rLL04Lk/
Or, if you want to work around some of the idiosyncrasies of .toFixed(), you can use this:
return (Math.round(_self.userInput() * 100) / 100).toFixed(2);
Working jsFiddle: https://jsfiddle.net/jfriend00/xx2aj2L0/
This solution passes all three of your test cases.

It's not Math.floor() that causes the problem, it is the inexactness of the floating point arithmetic. When you multiply 8192.8 by 100, you get 819279.9999999999.
Perhaps you should just manipulate it as a string:
function floorString(str) {
var pos = str.indexOf('.');
return (pos >= 0) ? ((str + '00').slice(0, pos + 3)) : (str + '.00');
}
jsfiddle

The order of your floor/parse seems out of order to me.
Try:
return Math.floor(parseFloat(_self.userInput())).toFixed(2);
Though be aware that 1.999999999999999999999999999999 gives 2.00 using the above; this is because floating point numbers aren't able to represent all values precisely.

another one without using a Math function (2 lines without formatting)
function floorString(str) {
var matches = str.match(/([\d]+(\.[\d]{0,2}))/);
return matches === null || matches[2].length === 1 ?
(str + ".00").replace("..", ".") :
matches[2].length < 3 ?
matches[0] + "00".substr(3 - matches[2].length) :
matches[0];
}

Related

Javascript: parseInt issue when it comes to math operators

I have this slider that on 'slide' grabs pricing values (string with euro symbol) from an array, later parsed (parseInt) for the html output. This works fine, on the output i get the number plus the symbol, but when I need to multiply those values (switch month pricing to anual), I loose the output, no numbers or symbol. So I guess the parseInt is working properly without a math operator...? I've serached for similar issues but I couldn't find any solution and it just got me more confused...Can anyone tell what I'm missing? The code:
var priceStarter = [
'149€',
'199€',
'249€',
'399€',
'599€',
'999€',
'Contact Us',
];
slider.on('slide', function (values, handle) {
if(jQuery(this).hasClass('active-anual')){
//ANUAL
var anualStarter = priceStarter[parseInt(handle)];
priceValueStarter.html(anualStarter * 10); //ISSUE HERE
}
else {
//MONTHLY
priceValueStarter.html(priceStarter[parseInt(handle)]); //WORKS
}
});
//TOGGLE ANUAL/MONTHLY
anual.on('click',function(){
jQuery(this).toggleClass('active-tab');
monthly.removeClass('active-tab');
slider.addClass('active-anual');
})
monthly.on('click', function () {
jQuery(this).toggleClass('active-tab');
anual.removeClass('active-tab');
slider.removeClass('active-anual');
})
}
EDIT:
This console.log(anualStarter) gives me the correct value but this console.log(anualStarter*10) gives me NaN
EDIT: based on Constantiner's answer, I get the numbers but I still loose the euro symbol and the contact us when using the operator
slider.on('slide', function (values, handle) {
if (jQuery(this).hasClass('active-anual')) {
//ANUAL
var anualStarter = priceStarter[parseInt(handle)];
priceValueStarter.html(parseInt(anualStarter )*10);
} else {
//MONTHLY
priceValueStarter.html(priceStarter[parseInt(handle)]);
}
})
;
Your priceStarter[parseInt(handle)] is a string like "249€". So you can't use anualStarter * 10 ("249€" * 10) — it is NaN. Try to use parseInt(anualStarter) * 10 instead.
A little explanation. When you try to use "249€" * 10 JavaScript engine tries to cast the string "249€" as a Number and doesn't interpret it as integer or something. So your "249€" * 10 is the same as Number("249€") * 10 and Number("249€") is NaN.
I suppose you planned to write some code like the following:
slider.on('slide', function (values, handle) {
if(jQuery(this).hasClass('active-anual')){
//ANUAL
var anualStarter = priceStarter[parseInt(handle)];
priceValueStarter.html(isNaN(parseInt(anualStarter)) ? anualStarter : parseInt(anualStarter) * 10 + "€"); //ISSUE HERE
}
else {
//MONTHLY
priceValueStarter.html(priceStarter[parseInt(handle)]); //WORKS
}
});

Regex to separate thousands with comma and keep two decimals

I recently came up with this code while answering another StackOverflow question. Basically, on blur, this code will properly comma separate by thousands and leave the decimal at two digits (like how USD is written [7,745.56]).
I was wondering if there is more concise way of using regex to , separate and cut off excessive decimal places. I recently updated this post with my most recent attempt. Is there a better way of doing this with regex?
Input -> Target Output
7456 -> 7,456
45345 -> 45,345
25.23523534 -> 25.23
3333.239 -> 3,333.23
234.99 -> 234.99
2300.99 -> 2,300.99
23123123123.22 -> 23,123,123,123.22
Current Regex
var result;
var str = []
reg = new RegExp(/(\d*(\d{2}\.)|\d{1,3})/, "gi");
reversed = "9515321312.2323432".split("").reverse().join("")
while (result = reg.exec(reversed)) {
str.push(result[2] ? result[2] : result[0])
}
console.log(str.join(",").split("").reverse().join("").replace(",.","."))
As an alternative to the Regex, you could use the following approach
Number(num.toFixed(2)).toLocaleString('en-US')
or
num.toLocaleString('en-US', {maximumFractionDigits: 2})
You would still have the toFixed(2), but it's quite clean. toFixed(2) though won't floor the number like you want. Same with {maximumFractionDigits: 2} as the second parameter to toLocaleString as well.
var nums = [7456, 45345, 25.23523534, 3333.239, 234.99, 2300.99, 23123123123.22]
for (var num of nums)
console.log(num, '->', Number(num.toFixed(2)).toLocaleString('en-US') )
Flooring the number like you showed is a bit tricky. Doing something like (num * 100 | 0) / 100 does not work. The calculation loses precision (e.g. .99 will become .98 in certain situations). (also |0 wouldn't work with larger numbers but even Math.floor() has the precision problem).
The solution would be to treat the numbers like strings.
function format(num) {
var num = num.toLocaleString('en-US')
var end = num.indexOf('.') < 0 ? num.length : num.indexOf('.') + 3
return num.substring(0, end)
}
var nums = [7456, 45345, 25.23523534, 3333.239, 234.99, 2300.99, 23123123123.22]
for (var num of nums) console.log(num, '->', format(num))
function format(num) {
var num = num.toLocaleString('en-US')
var end = num.indexOf('.') < 0 ? num.length : num.indexOf('.') + 3
return num.substring(0, end)
}
(when changing to another format than 'en-US' pay attention to the . in numbers as some languages use a , as fractal separator)
For Compatibility, according to CanIUse toLocaleString('en-US') is
supported in effectively all browsers (since IE6+, Firefox 2+, Chrome
1+ etc)
If you really insist on doing this purely in regex (and truncate instead of round the fractional digits), the only solution I can think of is to use a replacement function as the second argument to .replace():
('' + num).replace(
/(\d)(?=(?:\d{3})+(?:\.|$))|(\.\d\d?)\d*$/g,
function(m, s1, s2){
return s2 || (s1 + ',');
}
);
This makes all your test cases pass:
function format(num){
return ('' + num).replace(
/(\d)(?=(?:\d{3})+(?:\.|$))|(\.\d\d?)\d*$/g,
function(m, s1, s2){
return s2 || (s1 + ',');
}
);
}
test(7456, "7,456");
test(45345, "45,345");
test(25.23523534, "25.23"); //truncated, not rounded
test(3333.239, "3,333.23"); //truncated, not rounded
test(234.99, "234.99");
test(2300.99, "2,300.99");
test(23123123123.22, "23,123,123,123.22");
function test(num, expected){
var actual = format(num);
console.log(num + ' -> ' + expected + ' => ' + actual + ': ' +
(actual === expected ? 'passed' : 'failed')
);
}
I added another layer where regex that drops the unwanted decimals below hundredths on top of your regex comma adding logic;
val.replace(/(\.\d{2})\d*/, "$1").replace(/(\d)(?=(\d{3})+\b)/g, "$1,")
doIt("7456");
doIt("45345");
doIt("25.23523534");
doIt("3333.239");
doIt("234.99");
doIt("2300.99");
doIt("23123123123.22");
doIt("5812090285.2817481974897");
function doIt(val) {
console.log(val + " -> " + val.replace(/(\.\d{2})\d*/, "$1").replace(/(\d)(?=(\d{3})+\b)/g, "$1,"));
}
If multiple calls of regex replace is OK, this answer should satisfy you, since it is only has regex replace logic and nothing else.
Try:
var n = 5812090285.2817481974897;
n = n.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
console.log(n);
Outputs:
5,812,090,285.28
Note: .toFixed(2) returns a string. So in order to simplify this further you must add a way to turn n into a string before executing your regex. For example:
n.toString.replace(/(\d)(?=(\d{3})+\.)/g, '$1,'); //ofc with the additional regex
Although you would think it wouldn't matter in javascript, it apparently does in this situation. So I dont know how much 'less' messy it would be to not use.
Here is a way to do it without a regular expression:
value.toLocaleString("en-US", { maximumFractionDigits: 2 })
function formatValue() {
var source = document.getElementById("source");
var output = document.getElementById("output");
var value = parseFloat(source.value);
output.innerText = value.toLocaleString("en-US", { maximumFractionDigits: 2 });
}
<input id="source" type="text" />
<button onclick="formatValue()">Format</button>
<div id="output"></div>
RegEx to rescue again!
My solution has two parts :
.toFixed : Used to limit the decimal limit
/(\d)(?=(\d\d\d)+(?!\d))/g : It makes use of back reference with three digits at a time
Here's everything put together :
// .toFixed((/\./g.test(num)) ? 2 : 0) it tests if the input number has any decimal places, if so limits it to 2 digits and if not, get's rid of it altogether by setting it to 0
num.toFixed((/\./g.test(num)) ? 2 : 0).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,"))
You can see it in action here :
var input = [7456, 45345, 25.23523534, 3333.239, 234.99, 2300.99, 23123123123.22]
input.forEach(function(num) {
$('div')
.append(
$('<p>').text(num + ' => ' +
num.toFixed( (/\./g.test(num))?2:0 ).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,"))
);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div> </div>
NOTE: I've only used jQuery to append the results
You can do like this
(parseFloat(num).toFixed(2)).replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,").replace(".00","")
Here just convert number to formatted number with rounded down to 2 decimal places and then remove the .00 if exist.
This can be one approach you can use.
var format = function (num) {
return (parseFloat(num).toFixed(2)).replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,").replace(".00","")
}
$(function () {
$("#principalAmtOut").blur(function (e) {
$(this).val(format($(this).val()));
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="principalAmtOut" type="text" />
You can use Intl.NumberFormat with style set to "decimal" and maximumFractionDigits set to 2 at options object passed at second parameter
const nums = [7456, 45345, 25.23523534, 3333.239, 234.99, 2300.99, 23123123123.22];
const formatOptions = {style:"decimal", maximumFractionDigits:2};
const formatter = new Intl.NumberFormat("en-US", formatOptions);
const formatNums = num => formatter.format(num);
let formattedNums = nums.map(formatNums);
console.log(formattedNums);
I found a solution based on #Pierre's answer without using of toFixed:
function format(n) {
n = +n;
var d = Math.round(n * 100) % 100;
return (Math.floor(n) + '').replace(/(\d)(?=(\d{3})+$)/g, '$1,') + (d > 9 ? '.' + d : d > 0 ? '.0' + d : '');
}
console.log(format(7456));
console.log(format(7456.0));
console.log(format(7456.1));
console.log(format(7456.01));
console.log(format(7456.001));
console.log(format(45345));
console.log(format(25.23523534));
console.log(format(3333.239));
console.log(format(234.99));
console.log(format(2300.99));
console.log(format(23123123123.22));
console.log(format('23123123123.22'));

Trying to generate a random integer within a specific range then have it displayed inline

So, here's what I have (I am running JQuery):
http://jsfiddle.net/KDmwn/111/
The computer chose <span id="x"></span>.
$(document).ready(function () {
var x = function getRandomInt(1, 4) {
return Math.floor(Math.random() * (4 - 1 + 1)) + 1
};
$('#x').html(x);
}
I feel like the issue has to do with this $('#x').html(x);
Any help is much appreciated. Thanks!
The issue is because your x function has two integers set as the arguments, which is syntactically incorrect.
To achieve what you need you should remove the integers in the argument list, fix the mis-matched bracket at the end of the DOMReady handler and you can also remove the - 1 + 1 from the Math.random value.
Try this:
var x = function getRandomInt() {
return Math.floor(Math.random() * 4) + 1;
}
$('#x').html(x);
Updated fiddle
Use below code
$('#x').html(Math.floor(Math.random() * 4 ) + 1);
Fiddle

How to add one to a variable to make 2 not 11

Currently this makes 11. It's for a slideshow and the var "n" equals 1 by default
function forward() {
document.getElementsByClassName("img")[0].setAttribute("class","imgout");
setTimeout( function() {
var n1 = document.getElementById("img").getAttribute("data-number");
var n=n1+1;
document.getElementById("img").setAttribute("data-number", n);
document.getElementById("img").setAttribute("src", "images/" + n + ".jpg");
document.getElementsByClassName("imgout")[0].setAttribute("class","img");
}, 500)
}
Use parseInt():
var n = parseInt(n1, 10) + 1;
Instead of:
var n=n1+1;
When n1 will be a string, because it came from the DOM, you need to convert n1 to an integer. There are many ways to do this, and really you should probably use a regular expression to validate that n1 contains what you expect first, but that being said you can try any of the following:
var n=parseInt(n1, 10)+1;
var n=(n1*1)+1;
var n=(+n1)+1;
As an aside the regex for validating the input from the DOM might be something such as:
/^-?\d+$/
Use Number():
var n = Number("1");
FIDDLE
parseInt - good choice. Also you can do
var n = n-0+1
Just remember that of you have different types "+" will convert to string and "-" will convert to numbers
Convert n1 to number as it is a string like ~~n1. The ~~n1 form is good if you know you want an integer (a 32-bit integer).
Second way is to use Number() function i.e. Number(n1) will convert n1 value into a string.
var n=parseInt(n)+1;
Documentation
Fiddle

JavaScript Calculation Problem - NaN message

I'm calculating a total number. I get the sum values from div's. But in total, instead of numbers I get (NaN - Not a Number)
JavaScript Function:
<script type="text/javascript">
function calculateTotal(){
var total = document.getElementById('valor1').innerHTML*1 + document.getElementById('valor2').innerHTML*1 + document.getElementById('valor3').innerHTML*1 + document.getElementById('valor4').innerHTML*1 + document.getElementById('valor5').innerHTML*1 + document.getElementById('valor6').innerHTML*1;
document.getElementById('total').innerHTML = total;
}
</script>
EDIT:
I found the error, I had a closing tag inside the DIV's like this:
<center><div id="valor1"></center></div>
Changed to:
<center><div id="valor1"></div></center>
You cannot use document.getElementById('valor1').innerHTML directly. You have to convert this to number. Please try this.
var value = document.getElementById('valor1').innerHTML;
var number = parseFloat(value)||0;
Do this for each div innerHTML which have number.
var number = parseFloat(value)||0;
The above line will help you to assign 0 to value if div is empty or div html cannot be converted to a number.
Use parseFloat(document.getElementById('x').innerHTML) to convert them to numbers before performing operations:
var total = parseFloat(document.getElementById('x1').innerHTML) + parseFloat(document.getElementById('x2').innerHTML);
You also may want to check them if they're numeric, here's a simple test using isNaN:
alert((isNaN("23"))?'not number':'number');
HTML:
<div id="valor1">2</div>
<div id="valor2">2</div>
<div id="valor3">ccccc</div>
<div id="valor4">2</div>
<div id="valor5">2</div>
<div id="valor6">2</div>
<hr/>
<div id="total">0</div>
JavaScript:
function $(id) { return document.getElementById(id); }
function get(elem) { return parseFloat($(elem).innerHTML) || 0; }
(function() {
var total =
get('valor1') * 1 + get('valor2') * 1 + get('valor3') * 1 +
get('valor4') * 1 + get('valor5') * 1 + get('valor6') * 1;
$('total').innerHTML = total;
}());
A little optimization of the work and demo.
But why stop here? :) we can make it even better ( I think ):
function get(elem) {
return (parseFloat($(elem).innerHTML) || (function() {
$(elem).innerHTML += " <i>Not a number assumed 0</i>";
return 0;
}()));
}
And the updated demo.
Edit: no errors on Chrome & Mozilla (Linux).
try using parseInt() as in
var total = parseInt(document.getElementById('valor1').innerHTML)*1 + parseInt(document.getElementById('valor2').innerHTML)*1 + ... ;
etc etc
this will ensure that what you're getting out of the fields is in fact, a number
Did you try to put these parts into brackets?
(document.getElementById('valor1').innerHTML * 1) + ...
See: http://rx4ajax-jscore.com/ecmacore/operator/predence.html
Even better - use the parseInt(var string) function;
parseInt(document.getElementById('valor1').innerHTML) + ...

Categories

Resources