Testing for Rounding Errors in JavaScript - javascript

How can I determine if a number is too big (will cause rounding errors if math is performed) in JavaScript.
For example, I have a function that formats percentages. If it cannot format the passed in value correctly, I want it to return the value exactly as it was passed in.
function formatPercent(x, decimals) {
var n = parseFloat(x); // Parse (string or number)
if ($.isNumeric(n) === false) {
return x; // Return original if not a number
} else {
return n.toFixed(decimals) + '%'; // Return formatted string
}
};
alert(formatPercent(276403573577891842, 2)); // returns 276403573577891840.00%
Since formatting such a huge number is a corner case and not expected, I'd prefer to just return the number as it was passed in. What is the limit before the rounding errors start and how would I check for them?
Update:
What is JavaScript's highest integer value that a Number can go to without losing precision? says precision works up to +/- 9007199254740992. I am testing to see if that is all I need to check against to safely fail and return the passed in value unmodified.

If you always pass x in as a string, that will ensure that there are no rounding errors. The problem is that 276403573577891842 is being rounded right as the number literal is parsed, but if you use strings, that will never happen. Try doing this:
function formatPercent(x, decimals) {
if(typeof x != "string" && typeof x != "number") return x;
x = x+"";//convert to string if it is a number
var r = /^(?:(\d+)(\.\d*)?|(\d*)(\.\d+))$/;// RegExp for matching numerical strings
return x.replace(r, function(match, int, dec){
if(decimals>0){
int = (typeof int == "string"?int:"");//if passed string didn't have integers
dec = (typeof dec == "string"?dec:".");//if passed string didn't have decimals
while(dec.length-1<decimals) dec += "0";//pad zeroes until dec.length-1==decimals
return int+dec.slice(0,decimals+1)+"%";//in case dec.length-1>decimals
}
int = (typeof int == "string"?int:"0");//if passed string didn't have integers
return int+"%";
});
// Return formatted string or original string conversion if no match found
}
alert(formatPercent("276403573577891842", 1));// returns 276403573577891842.0%
alert(formatPercent("276403573577891842.55", 1));// returns 276403573577891842.5%
alert(formatPercent("276403573577891842.55", 0));// returns 276403573577891842%
alert(formatPercent(".55", 1));//returns .5%
alert(formatPercent(".55", 0));//returns 0%
alert(formatPercent(276403573577891842, 1));// returns 276403573577891840.0%
alert(formatPercent("this is not a number", 2));// returns this is not a number
alert(formatPercent({key:"not number or string"}, 2));// returns the object as it was
Even though formatPercent still fails in the case of passing a number, this will prevent rounding error from passed strings. Please note this is not incorrect, as the only case in which it will fail is when a number that is too large is hard-coded as a parameter.

function formatPercent(x, decimals) {
var n = parseFloat(x); // Parse (string or number)
if ($.isNumeric(n) === false) {
return x; // Return original if not a number
} else {
var d = Math.pow(10, decimals)
return (Math.round(n * d) / d).toString() + "%";
}
};
Using rounding will drop the decimal if it's .00

This is what I used to catch too big or too small integers:
function getSafeNumber(x) {
var n = parseFloat(x); // Parse and return a floating point number
if ($.isNumeric(n) === false ||
n >= 9007199254740992 ||
n <= -9007199254740992) {
return false; // Not numeric or too big or too small
} else {
return n; // return as number
}
};

No greater than nine quardbillion will cause this type of error due to internal representation of no

Related

What is the safest way to split a "Floating Point Number String" into its Whole and Fractional Parts

I have developed the following short function to split a number passed to it in a string format into its Whole and Fractional Parts (also outputted as strings).
Note1: The Whole part of the number can run into large numbers (in excess of 50 digits).
Note 2: The output result (the Whole and the Fraction strings) will not be converted into an integer or a float but will be further manipulated only as a string due to the Javascript rounding for large numbers. So going back to numbers is not a concern here
Because the function detects the machine's locale automatically, it is therefore expected that the user enters (passes) the number in the locale of his local machine or otherwise the number is generated programmatically and passed to the function.
The number is expected to be passed as a "string" due to the very large length and also because there is no handling for numbers passed in exponent (e) format.
The function uses the toLocaleString() to detect the decimal and thousand separators.
I have tested the function with the major number systems (.,' space) and so far, so good.
The question is, how safe will this code be, and are there any alternative better and safer methods to do it or corrections/changes necessary?
Thanks
function splitFloatString(NumString) {
var decimalSeparator = (1.1).toLocaleString().substring(1,2); // Get Decimal Separator
var thousandSeparator = (1000).toLocaleString().substring(1,2); // Get Thousands Separator
NumString += ""; // ensure a string
var fraction ="0"; // default no fractional part
NumString = NumString.replace(RegExp("\\"+thousandSeparator,"g"),""); //remove thousand separators if any
if (RegExp("\\"+decimalSeparator,"g").test(NumString)) { // test for decimal separator
var n = NumString.split(decimalSeparator); // Split at Decimal Seprator
NumString = n[0]; // The Whole part
if (n.length==2) fraction = n[1]; // The Fractional part
if (fraction=="") fraction ="0";
}
console.log("Whole: ("+NumString+"), Fraction: ("+fraction+")"); // added for testing
//return n=[NumString,fraction]; // normal return uncomment
}
//------------------------------------------------------------------------------------
// Tests assuming user's machine and user enters/passes US-EN separators as an example
//------------------------------------------------------------------------------------
splitFloatString("123456789123456699999887788812340786.45678907656912574321115194123123456789");
splitFloatString("1,234,567,891,234,566,999,998,888,812,340.456520754186789075194123123456789");
splitFloatString("200")
splitFloatString("0.")
splitFloatString(123456.2349999)
splitFloatString("")
splitFloatString()
According to me, you are just complicating the whole thing unnecessarily. Here's a simple way to achieve the same result.
function getLocaleSeparators() {
const testNumber = 1000.1.toLocaleString()
return [testNumber.substring(1,2), testNumber.substring(5,6)]
}
function splitFloatString(number) {
const numberString = number.toString()
const [thousandSeparator, decimalSeparator] = getLocaleSeparators()
let [wholePart, fractionPart] = numberString.replace(new RegExp(thousandSeparator, 'g'), '').split(decimalSeparator)
wholePart = wholePart || "0"
fractionPart = fractionPart || "0"
console.log(`Whole: ${wholePart}, Fraction: ${fractionPart}`);
}
function getLocaleSeparators() {
const testNumber = 1000.1.toLocaleString()
return [testNumber.substring(1,2), testNumber.substring(5,6)]
}
function splitFloatString(number) {
const numberString = number.toString()
const [thousandSeparator, decimalSeparator] = getLocaleSeparators()
let [wholePart, fractionPart] = numberString.replace(new RegExp(thousandSeparator, 'g'), '').split(decimalSeparator)
wholePart = wholePart || "0"
fractionPart = fractionPart || "0"
console.log(`Whole: ${wholePart}, Fraction: ${fractionPart}`);
}
splitFloatString("123456789123456699999887788812340786.45678907656912574321115194123123456789");
splitFloatString("1,234,567,891,234,566,999,998,888,812,340.456520754186789075194123123456789");
splitFloatString("200")
splitFloatString("0.")
splitFloatString(123456.2349999)
splitFloatString("")
I recommend you to use the math.floor function for such purposes.
It rounds the number passed as parameter to its nearest integer in downward direction:
Also, The Math.ceil() function rounds a number up to the next largest whole number or integer.
For other handy options, you can check out this https://www.w3docs.com/snippets/javascript/how-to-convert-a-float-number-to-whole-number-in-javascript.html
Please try with this one. might help you.
number = "21212.32323";
var numberString = number + ''; // converts into string
var index = numberString.lastIndexOf("."); // get the index
var strFloat, wholeStr;
if(index === -1) {
strFloat = '';
wholeStr = numberString.replace(/[^\w\s]/gi, '').replace(/ /g,'')
} else {
strFloat = numberString.substring(index + 1); // get the decimal part
var strWhole = numberString.substring(0, index); // get the number
wholeStr = strWhole.replace(/[^\w\s]/gi, '').replace(/ /g,'') // remove spcl character
}
console.log(`Whole: ${wholeStr}, Fraction: ${strFloat}`);
And yes you can not exceed MAX_SAFE_INTEGER in javascript i.e 32-bits.
What is JavaScript's highest integer value that a number can go to without losing precision?
Have been able to finally get the final answer I was looking for.
Thanks to all for their assistance and ideas:
function numberSplit (NumString) {
var decimalSep = 1.1.toLocaleString().substring(1,2), // Get Deciaml Separator
thousandSep = 1e3.toLocaleString().substring(1,2), // Get Thousand Separator
fraction = "0", // Default "0"
n = (NumString = (NumString +="").replace(RegExp("\\"+thousandSep,"g"),"")).split(decimalSep);
NumString = n[0] ; // Get Whole Part
NumString == "" && (NumString = undefined); // Whole = undefined if empty
n.length == 2 && (fraction = n[1]); // Get Fractional part (only if 1 decimal place)
fraction == "" && (fraction = "0"); // Fraction = 0 if empty
console.log("Whole: ("+NumString+"), Fraction: ("+fraction+")")
}
//-----------------------------------------------------------
// Ttests assuming user enters US-EN separators as an example
//-----------------------------------------------------------
numberSplit("123456789123456699999887788812340786.45678907656912574321115194123123456789");
numberSplit("1,234,567,891,234,566,999,998,888,812,340.456520754186789075194123123456789");
numberSplit("200")
numberSplit("0.")
numberSplit(123456.2349999)
numberSplit("1.2.3"); // Fractional part ignored as invalid
numberSplit("") // Return undefined
numberSplit() // Return undefined
numberSplit(NaN) // Return NaN
numberSplit(undefined) // Return undefined

Counting numbers after decimal point in JavaScript

I have a problem in JavaScript. Is it possible to check how many numbers are after the decimal point? I tried to do it using a.toString().split(".")[1]), but if there is no decimal point in the number, there is an error. What should I do if I want the system to do nothing if there is no decimal point?
You're on the right track. You can also .includes('.') to test if it contains a decimal along with .length to return the length of the decimal portion.
function decimalCount (number) {
// Convert to String
const numberAsString = number.toString();
// String Contains Decimal
if (numberAsString.includes('.')) {
return numberAsString.split('.')[1].length;
}
// String Does Not Contain Decimal
return 0;
}
console.log(decimalCount(1.123456789)) // 9
console.log(decimalCount(123456789)) // 0
Convert to a string, split on “.”, then when there is no “.” to split on, assume it’s empty string '' (the part you’re missing), then get said string’s length:
function numDigitsAfterDecimal(x) {
var afterDecimalStr = x.toString().split('.')[1] || ''
return afterDecimalStr.length
}
console.log(numDigitsAfterDecimal(1.23456))
console.log(numDigitsAfterDecimal(0))
You could check if no dot is available, then return zero, otherwise return the delta of the lenght and index with an adjustment.
function getDigits(v) {
var s = v.toString(),
i = s.indexOf('.') + 1;
return i && s.length - i;
}
console.log(getDigits(0));
console.log(getDigits(0.002));
console.log(getDigits(7.654321));
console.log(getDigits(1234567890.654321));
The condition you need is:
number.split('.')[1].length
It checks if there are any numbers after the dot which separates the number from its decimal part.
I'm not sure if you are able to use split on numbers though. If not, parse it to a string.
You first need to convert the decimal number to string and then get the count of character after decimal point,
var a = 10.4578;
var str = a.toString();
if(str){
var val = str.split('.');
if(val && val.length == 2){
alert('Length of number after decimal point is ', val[1].length);
} else {
alert('Not a decimal number');
}
}
The output is 4

Use of arguments causing function to always return false

I would like to sum the args of my function if and only if the two args are numbers (hence my first function).
function checkNum() {
var num = 0;
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] !== 'number') {
return false;
}
}
return true;
}
function addTogether() {
var num = 100;
if ( checkNum() ) {
return arguments[0] + arguments[1];
} else {
return undefined;
}
}
addTogether(2, "");
However my second function performs the sum no matter what the args values are. Any hints on how to fix this ?
checkNum() isn't declared to explicitly take any arguments (which implies to anyone looking at the function that none are expected) and you are not sending any when you call it, so arguments.length is always 0, you never enter into your loop body and you always return true.
Your second function is called by passing two arguments, so your references to arguments[0] and arguments[1] are valid there. But, even still, the use of arguments isn't really meant for all argument passing.
It's best to set up your functions with named parameters and then you can access them via those names. The use of arguments (while valid), is not encouraged as the default mechanism for accessing arguments. It's generally used for validation (ensure that the correct amount of parameters were passed to the function before the function attempts to operate on them, for example).
Also, it's best to test for numbers with a regular expression because typeof can "lie" to you. For example:
// Would you ever think that not a number is of type "number"?!
console.log(typeof NaN === "number");
Now, depending on your criteria for "number", there are two ways you could go.
Only numeric digits are allowed (i.e. 6 is allowed, "6" is not)
// It's better for this function to test one number
// at a time, so you can react to that particular
// success or failure
function checkNum(num) {
// No loop and no if/then needed, just return
// whether the argument is a number, but don't
// test for typeof number because typeof NaN === "number"
// Use a regular expression instead
var reg = /[0-9]+$/; // digits or strings of characters that are from 0 - 9
// Test for only digits not numbers passed as strings
// For example 6 is good, "6" is bad. Here, the use of "typeof"
// is safe because you are also testing that the input is digits
// or characters from 0 to 9 (NaN wouldn't pass this test)
return reg.test(num) && typeof num === "number"; // true or false will be returned
}
function addTogether(val1, val2) {
// Test each input, independantly so that you can react more granularly
if ( checkNum(val1) && checkNum(val2) ) {
return val1 + val2;
}
// It's not necessary to have an "else" that returns undefined because
// that's what will happen as long as you don't return anything else.
}
console.log(addTogether(2, "")); // undefined
console.log(addTogether(2, 6)); // 8
console.log(addTogether(2, "6")); // undefined because "6" is a string, not a digit
Numeric digits and numeric characters are allowed (i.e. 6 and "6" are allowed). In this case, you'd need to ensure that numeric characters get converted to numbers before addition is done so you get mathematical addition and not string concatenation.
// It's better for this function to test one number
// at a time, so you can react to that particular
// success or failure
function checkNum(num) {
// No loop and no if/then needed, just return
// whether the argument is a number, but don't
// test for typeof number because typeof NaN === "number"
// Use a regular expression instead
var reg = /[0-9]+$/; // digits or strings that are from 0 - 9
// Test for only digits and numbers passed as strings
return reg.test(num); // true or false will be returned
}
function addTogether(val1, val2) {
if ( checkNum(val1) && checkNum(val2) ) {
// If checkNum returns true for numeric characters as well as digits, then
// you'd need to ensure that the characters get converted to numbers so that
// you get mathmatical addition and not string concatenation. That would be done like this:
return +val1 + +val2
}
// It's not necessary to have an "else" that returns undefined because
// that's what will happen as long as you don't return anything else.
}
console.log(addTogether(2, "")); // undefined
console.log(addTogether(2, 6)); // 8
console.log(addTogether(2, "6")); // 8 because "6" is converted to 6, not a string of "6"
The arguments array, as evaluated within checkNum, contains the arguments passed to checkNum. But you aren't passing any arguments to checkNum. Try changing the if statement to
if ( checkNum(arguments[0], arguments[1]) )
You aren't passing any arguments to checkNum. You can fix this with apply:
// ...
if (checkNum.apply(this, arguments)) {
// ...
Edit: That would allow you to check any number of arguments passed to addTogether. If you only want to allow for two arguments, you can used named parameters:
function checkNum(a, b) {
return typeof a === 'number' && typeof b === 'number';
}
function addTogether(a, b) {
if (checkNum(a, b)) return a + b;
else return undefined; // line not needed
}
addTogether(2, "");

Adding a string (with only digits in it) to a number results in a number (according to isNaN) which looks like it should have been a string?

1) Why the 155100 is a number here? Just like 255 would be if var s = 155;.
2) Why the 155100 is still a number even if var n = "100"; ?
3) Where and why is var res converted to a number?
What am I missing here?
var s = "155";
var n = 100;
var res = s + n;
document.write(res + "<hr>");
if ( isNaN(res) ) {
document.write("It is not a number");
}
if ( !isNaN(res) ) {
document.write("It is a number");
}
<html>
<head>
<title>stackoverflow.com</title>
</head>
<body>
<script src="script.js"></script>
</body>
</html>
Thank you so much!
To your questions:
Why the 155100 is a number here? Just like 255 would be if var s = 155;?
It is a string, but when passed to isNaN, that function coerces it to a number and then returns whether that coercion resulted in NaN.
Why the 155100 is still a number even if var n = "100";?
See above. It is not.
Where and why is var res converted to a number?
It is not -- it remains a string.
But as documented on MDN:
When the argument to the isNaN function is not of type Number, the value is first coerced to a Number.
Notes
To check if a variable is of the Number type:
if (typeof res === 'number')
To convert a string to a number, one of these:
i = +res;
i = Number(res);
If you want to only check the start of a string, and not fail if any subsequent characters are not numeric (like 123.45abc), then use:
i = parseFloat(res);
If your interest is only in integers, you can use parseInt, preferably with the second argument to indicate the base. Like with parseFloat, it does not return NaN as soon as the first character passes the test:
i = parseInt(res, 10);
See the MDN link here.
isNaN(res) ---> will be false because 155100 is numeric
typeof res ---> string
Go ahead - try it in your Chrome console right now.
typeof "155100"
isNaN will always return false for a string value regardless of whether or not it can be parsed into a valid number. In order to check if your string is a valid number, you can parse it with parseInt and check if the result is NaN:
var s = "155";
var n = 100;
var res = s + n;
document.write(res + "<hr>");
if ( isNaN(parseInt(res)) ) {
document.write("It is not a number");
} else {
document.write("It is a number");
}
Note: parseInt will only return NaN if the first character cannot be converted to a number. MDN has a "stricter" version here that returns NaN if any part of the string cannot be converted .
JavaScript is a language based upon loose type-interpretation, instead of implicitly requiring type declaration or throwing an error else-wise;
When JavaScript gets a something in quotes, it determines it to be a string; the + operator, with the type now being String, is understood by JavaScript in this context as a string concatenater (concatenation is the combination of two or more things things) and so it happily appends the two together.
Here, you need to do what is known as Type Casting or Type Throwing where you throw(or cast) something into a different type. This is necessary here so that your + operator will behave as you desire
For Example:
var str = "3.14";
var num = new Number(str);
The new keyword is optional, but is recommended for source clarity and readability.

How to remove a char from a string, and return a number from the new string - Javascript

I have a few functions that aim to convert a user input as a string to a binary number. This is part of a basic 'assembler' program I am working on. Here is my code:
function generateBinaryNumber(inputValue) //only works for hash!
{
var number;
if(containsHash(inputValue) != -1)
{
number = getNumberFromString(inputValue);
}
return decimalToBinary(number);
}
function containsHash(number) //Working!
{
var n = number.indexOf('#');
return n;
}
function getNumberFromString(mixedInput) //Working!
{
return (mixedInput.replace('#',''));
}
function decimalToBinary(decimal) //Working!
{
decimal = parseInt(decimal);
return decimal.toString(2);
}
If a user enters a value of '5' for example, the function returns NaN - however if they specify #5 it works - 101 is returned. I am new to programming and Javascript, if anyone can point me in the right direction that would be great. Thanks!
You're not passing the input if there's no #
function generateBinaryNumber(inputValue) //only works for hash!
{
var number; //number is not set
if(containsHash(inputValue) != -1)
{
//number is set here, but only if the if statement is true
number = getNumberFromString(inputValue);
}
//if containsHash was false, number is undefined
return decimalToBinary(number);
}
You just want to say var number=inputValue; when you declare it
As a side note, you should also add a radix argument to your parseInt (so something like parseInt(decimal,10)) otherwise it will treat input like "010" as hex digits and will parse it incorrectly.

Categories

Resources