Round all numbers in the string - javascript

I've got formula strings like:
let a = '554898128.3227372 + (-1.35880 * (A * 12)) + (1.1171758319197438 * (B * 13)) + (0.8813173184360715 * (x + 1))';
or
let b = '10867.306445101707 + (1.6367961676820935 * (X + 1))';
I want to round numbers in the formula to three decimals. What's the fastest way to do that? I thought about using a.split(' '); to split my string to array and checking if it's number, but what do I do about the round brackets? The formula has to remain the same just with rounded values afterwards

A crude way to do that would be to extract everything that looks like a number and apply toFixed.
let a = '554898128.3227372 + (-1.35880 * (A * 12)) + (1.1171758319197438 * (B * 13)) + (0.8813173184360715 * (x + 1))';
a = a.replace(/-?\d+(\.\d+)?/g, n => Number(n).toFixed(3))
console.log(a)
A better, but far more complicated, option is to parse the string and replace the numbers in the parse tree. Since I presume you're going to need this anyways, to evaluate the formula, this might be actually the way to go.

I'd use a regular expression to match numeric characters with decimals, capturing everything up to the first 3 decimals, and replace with that captured group:
let a = '554898128.3227372 + (-1.35880 * (A * 12)) + (1.1171758319197438 * (B * 13)) + (0.8813173184360715 * (x + 1))';
console.log(
a.replace(/(\d+\.\d{3})\d+/g, '$1')
);

Split won't do a trick cause you'll have to handle all those non-numeric symbols and putting them back together.
Generic way of handling formulas is writing lexical analyzer or using some 3rd party for that.
Luckily here, you might just use replace method with regex that matches any number with period like this:
const a = '554898128.3227372 + (-1.35880 * (A * 12)) + (1.1171758319197438 * (B * 13)) + (0.8813173184360715 * (x + 1))';
const b = a.replace(/\d+\.\d+/g, function(n) {
return Math.round(Number(n));
});
console.log(b);

Related

Making math solver...NaN message

I am trying to make a simple quadratic formula solver for my own personal use. It works for the most part, but there's one problem: it only works when the answers are Rational numbers (i.e., it won't display sqrt(-1) because that's "i"). When it tries to perform the calculation and the answer isn't a rational, it will display "NaN". My code looks like this:
...*regular html*
<script type = "text/javascript">
var aValue = prompt("What is your 'a' value?");
var bValue = prompt("What's your 'b' value?");
var cValue = prompt("What's your 'c' value?");
var quadFinder = function x_finder(a,b,c) {
document.write((-1 * b + Math.sqrt(b*b - 4*a*c)) / 2*a);
document.write("<br>");
document.write((-1 * b - Math.sqrt(b*b - 4*a*c)) / 2*a);
};
quadFinder(aValue,bValue,cValue)
I know the function is all sound because it will work as long as the answer is only a number.
One other question: what is the Math. command that will round the number? I once put in a few numbers and it came out to some crazy number with around 10 decimal numbers after it.
You might try looking at the discriminant and catching complex cases:
var quadFinder = function x_finder(a,b,c) {
var disc = b * b - 4 * a * c;
if (disc >= 0){
document.write((-1 * b + Math.sqrt(b*b - 4*a*c)) / 2*a);
document.write("<br>");
document.write((-1 * b - Math.sqrt(b*b - 4*a*c)) / 2*a);
} else {
var real = (-1 * b) / (2 * a);
var complex = Math.sqrt(-disc)/(2 * a);
document.write(real + " + " + complex + "i");
document.write("<br>");
document.write(real + " - " + complex + "i");
};

Does this code work when comparing a floating point number to a string?

I was reading code in a library from Adobe (snap.svg.js) and I saw this code, which confused me:
function curveDim(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
c = p1x - c1x,
t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a,
t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
Math.abs(t1) > "1e12" && (t1 = .5);
Math.abs(t2) > "1e12" && (t2 = .5);
Since the formula is pretty complicated, I couldn't come up with values to test it, so I wanted to ask the general question: why can you compare Math.abs(t1) to a string '1e12'? Wouldn't that convert the floating point number to a string and then just do lexical comparison? What if the variable a is zero? Is this good code? Any explanation of what the author is trying to do and if it is correct would be great.
No, the > operator converts both operands to numbers (http://es5.github.io/#x11.8.2, http://es5.github.io/#x11.8.5).
See what happens when I convert the string to a number:
> +"1e12"
1000000000000
1e12 is actually a valid number literal and means 1 * 1012. No idea why they used strings instead, Math.abs(t1) > 1e12 would work just as fine.
You can easily test this yourself
var t1 = 10000000000000;
if (Math.abs(t1) > "1e12")
console.log('t1 is greater');
else
console.log('t1 is less or equal');
See JSFiddle

JQuery create a random 16 digit number possible?

As the title says ... is it possible to create a random 16 digit number with jquery?
Just use:
Math.floor(Math.random()*1E16)
EDIT :
Note that there is about a 1/10 chance of a lower number of digits. If Math.random() generates something like 0.0942104924071337 then 0.0942104924071337 * 1E16 is 0942104924071337 which evaluates to 942104924071337; a 15 digit number.
The only way to 100% guarantee that the number is 16 digits in length is to have it be formed as a string. Using this method I would recommend #rjmunro's answer:
number = (Math.random()+' ').substring(2,10)+(Math.random()+' ').substring(2,10);
Not with jQuery, no, but you can do it with plain javascript.
If you want exactly 16 digits (possibly including leading 0s), I would start with Math.random(), convert to a string, pick 8 digits, and concatenate 2 runs together.
number = (Math.random() + '').substring(2,10)
+ (Math.random() + '').substring(2,10);
No, use JAVASCRIPT!
jQuery is not some magic genie.
This is a task which is much better suited for raw javascript. For example
var str = '';
var i;
for (i = 0; i < 16; i++) {
var number = Math.floor(Math.random() * 10) % 10;
str += number;
}
I just tried with #rjmunro 's answer.
Unfortunately, it does generate string less than 16digits,
but very rare, approxly once in 10 million times.
Here is my testing code, runs in nodejs:
'use strict';
var fs = require('fs');
var totalTimes = 100000000;
var times = totalTimes;
var fileName;
var writeStream;
while (times > 0) {
var key = (Math.random() + ' ').substring(2,10) + (Math.random() + ' ').substring(2,10);
times --;
if (key.length !== 16) {
var msg = 'a flaw key gened: ' + key + '\n';
// create a log file at first time
if (!fileName) {
fileName = 'log/flaw_key_' + new Date() + '.txt';
}
writeStream = fs.createWriteStream(fileName);
writeStream.write(msg);
writeStream.end();
}
if (times === 0) {
console.log(totalTimes + ' times key gened');
}
}
Also #Dimitri Mikadze 's answer generate less length string as well, so I eventually adopt a way with some concept of his solution:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* Gen random digits string in specific length
* #param {Int} length of string
*
* #return {String}
*
*/
function genString(length) {
var times = length;
var key = '';
while (times > 0) {
times --;
key += getRandomInt(0, 9);
}
return key;
}
genString(16); // a 16 digits string
u can use this function to generate random digits, just pass minimum and maximum parameters
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
random 16 digit, usage
randomInt(0, 9999999999999999);
I know this question is old but this simple function will guarantee a 16 (or however many you want) character string every time without the 10% failure rate of other solutions. Can change it to a number if you need to.
function generate() {
let string = ""
while (string.length < 16) {
let number = Math.floor(Math.random() * 10).toString()
string += number
}
return string
}
I think this way is more beautiful:
const generateFixedLengthNumberInString = length =>
[...Array(length).keys()].reduce(
previousValue =>
previousValue + String(Math.floor(Math.random() * 10) % 10),
);
console.log(generateFixedLengthNumberInString(16))
// prints "0587139224228340"

Why is my JS random ints generator so wrong when I give him one negative and one positive number?

So I use such script for random int generation inside of range
function randomInRange(start, end)
{
if ((start >= 0) && (end >= 0))
{
return Math.round(Math.abs(start) + (Math.random() * (Math.abs(end) - Math.abs(start))));
}
else if ((start <= 0) && (end <= 0))
{
return 0 - (Math.round(Math.abs(start) + (Math.random() * (Math.abs(end) - Math.abs(start)))));
}
else
{
return Math.round(((start) + Math.random() * (end - start)));
}
}
You can see it at work here. for Positive ranges its correct, for negative its correct but I get bad and wrong results for mixed. Why and how to fix it?
I try to use formula like Math.round(start + Math.random() * (end - start));
If you use Math.round you will get an uneven distribution.
If you for example ask for numbers between 2 and 4, only the random numbers between 2.0 and 2.5 will be rounded to 2, and only the numbers between 3.5 and 4.0 will be rounded to 4, while the numbers between 2.5 and 3.5 will be rounded to 3. That means that 50% of the numbers will be 3, while only 25% will be 2 and 25% will be 4.
Use Math.floor instead to get an even distribution of the random numbers.
You don't have to check for the sign of the start and end, the expression end - start + 1 will be the size of the range with the sign different depending on which side of start it is.
function randomInRange(start, end) {
if (start > end) start++; else end++;
return Math.floor((start + Math.random() * (end - start)));
}
Demo: jsfiddle.net/guaEp/1/
Correction:
The expression end - start + 1 doesn't work if start > end. The expression end - start works if the upper bound is exclusive, i.e. the range -10 to 11 returns numbers between -10 and 10. I added code so that the function takes inclusive upper bound and converts it to an exclusive.
Also:
As davin pointed out, you are feeding the function strings, which will make the function concatenate the strings instead of doing arithmetic operations.
Just convert the strings to numbers using the parseInt function:
val.innerHTML = randomInRange(parseInt($('fnt').value), parseInt($('lint').value));
OK, I found the problem, you're performing algebra on strings (since in your code $('fnt').value is the value of an input box, which is a string), not numbers, so things like + will end up concatenating strings and not adding their numeric content. In your particular example, you have:
Math.round(((start) + Math.random() * (end - start)))
Which evaluates to:
Math.round((('13') + Math.random() * ('-666' - '13')))
Which evaluates to (for example):
Math.round("13-339.44615370430984")
Since '13' + '-339.44615370430984' will be concatenated, and finally the Math.round call will return NaN
You should have:
function randomInRange(start, end) {
start = Number(start); end = Number(end);
return Math.round(start + Math.random() * (end - start));
}
Or change the values you pass into the function, making sure they're numbers.
Simply
function randomInRange(start, end) {
return Math.round(start + Math.random() * (end - start));
}
should work.
jsFiddle Demo
UPDATE: As #Guffa correctly pointed out, distribution will not be even. You can use this:
return Math.floor(start-- + Math.random() * (end - start));
I'm too lazy to find the error in your code, but the calculation can be done much easier:
function randomInRange(start, end)
{
//Extra variables to make it easier to understand
var rangeBegin = Math.min(start,end);
var rangeEnd = Math.max(start,end);
var rangeSize = rangeEnd-rangeBegin+1;
var seed = Math.random();
var result = Math.floor((seed*rangeSize)+rangeBegin);
return result;
}

broken toFixed implementation [duplicate]

This question already has answers here:
Javascript toFixed Not Rounding
(23 answers)
Closed 2 years ago.
The default implementation of javascript's "Number.toFixed" appears to be a bit broken.
console.log((8.555).toFixed(2)); // returns 8.56
console.log((8.565).toFixed(2)); // returns 8.57
console.log((8.575).toFixed(2)); // returns 8.57
console.log((8.585).toFixed(2)); // returns 8.59
I need a rounding method that is more consistent than that.
In the range between 8.500 and 8.660 the following numbers don't round up correctly.
8.575
8.635
8.645
8.655
I've tried to fix the prototype implementation as follows, but it's only half way there. Can anyone suggest any change that would make it work more consistently?
Number.prototype.toFixed = function(decimalPlaces) {
var factor = Math.pow(10, decimalPlaces || 0);
var v = (Math.round(this * factor) / factor).toString();
if (v.indexOf('.') >= 0) {
return v + factor.toString().substr(v.length - v.indexOf('.'));
}
return v + '.' + factor.toString().substr(1);
};
This is because of floating-point errors.
Compare (8.575).toFixed(20) with (8.575).toFixed(3) and imagine this proposition: 8.575 < real("8.575"), where real is an imaginary function that creates a real number with infinite precision.
That is, the original number is not as expected and the inaccuracy has already been introduced.
One quick "workabout" I can think of is: Multiply by 1000 (or as appropriate), get the toFixed(0) of that (still has a limit, but it's absurd), then shove back in the decimal form.
Happy coding.
Thanks for the answer pst. My implementation almost worked, but didn't in some cases because of floating point errors.
this line in my function is the culprit:
Math.round(this * factor)
(it's on the Number.prototype, so "this" is the number);
8.575 * 100 comes out to 857.4999999999999, which in turn rounds down.
this is corrected by changing the line to read as follows:
Math.round(Math.round(this * factor * 100) / 100)
My entire workaround is now changed to:
Number.prototype.toFixed = function(decimalPlaces) {
var factor = Math.pow(10, decimalPlaces || 0);
var v = (Math.round(Math.round(this * factor * 100) / 100) / factor).toString();
if (v.indexOf('.') >= 0) {
return v + factor.toString().substr(v.length - v.indexOf('.'));
}
return v + '.' + factor.toString().substr(1);
};
A consistent solution would be to add a fixed tolerance (epsilon) to each number before rounding. It should be small, but not too small.
For example, with an eps = 1e-9, this:
console.log((8.555).toFixed(2)); // returns 8.56
console.log((8.565).toFixed(2)); // returns 8.57
console.log((8.575).toFixed(2)); // returns 8.57
console.log((8.585).toFixed(2)); // returns 8.59
Becomes this:
console.log((8.555 + eps).toFixed(2)); // returns 8.56
console.log((8.565 + eps).toFixed(2)); // returns 8.57
console.log((8.575 + eps).toFixed(2)); // returns 8.58
console.log((8.585 + eps).toFixed(2)); // returns 8.59
Maybe it will help someone, this is fixed popular formatMoney() function, but with correct roundings.
Number.prototype.formatMoney = function() {
var n = this,
decPlaces = 2,
decSeparator = ",",
thouSeparator = " ",
sign = n < 0 ? "-" : "",
i = parseInt(n = Math.abs(+n || 0)) + "",
j = (j = i.length) > 3 ? j % 3 : 0,
decimals = Number(Math.round(n +'e'+ decPlaces) +'e-'+ decPlaces).toFixed(decPlaces),
result = sign + (j ? i.substr(0, j) + thouSeparator : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thouSeparator) + (decPlaces ? decSeparator + Math.abs(decimals-i).toFixed(decPlaces).slice(2) : "");
return result;
};
(9.245).formatMoney(); // returns 9,25
(7.5).formatMoney(); // returns 7,50
(8.575).formatMoney(); // returns 8,58
Check my answer
function toFixed( num, precision ) {
return (+(Math.round(+(num + 'e' + precision)) + 'e' + -precision)).toFixed(precision);
}

Categories

Resources