For example,
1.toFixed(2) // Uncaught SyntaxError: Invalid or unexpected token
(1).toFixed(2) // "1.00"
let num = 1
num.toFixed(2) // "1.00"
At the same time, you don't have to wrap parenthesis around strings to call methods on them
'yo'.repeat(3) // "yoyoyo"
What is the rule at play here and where else does it apply? Guessing it has something to do with the dot being misinterpreted as a decimal for numbers?
Because the interpreter is looking for more digits (decimal values), not keywords or methods.
As others have stated, JavaScript is looking for more numbers after the decimal point. It thinks you are trying to type a float like 1.2 and it doesn't like that t there, it's not a number.
Interestingly, you can do this without parenthesis or making variable by using 2 decimal points. Like this: 1..toFixed(2). I guess you can also do 1.0.toFixed(2) if you want.
When using a variable, Javascript is confident that you're not going to add decimals after the point.
When not using a variable, Javascript thinks that you are going to add decimals after the point.
When you wrap your number with parenthesis, you say that the number is finished, and everything is fine again.
You can also use .. if you don't like parenthesis. The first one for decimals, the second one to call the method.
let num = 1;
console.log(num.toFixed(2));
// console.log(1.toFixed(2)); // ERROR
console.log((1).toFixed(2));
console.log(1..toFixed(2));
Related
This is an extension of this SO question
I made a function to see if i can correctly format any number. The answers below work on tools like https://regex101.com and https://regexr.com/, but not within my function(tried in node and browser):
const
const format = (num, regex) => String(num).replace(regex, '$1')
Basically given any whole number, it should not exceed 15 significant digits. Given any decimal, it should not exceed 2 decimal points.
so...
Now
format(0.12345678901234567890, /^\d{1,13}(\.\d{1,2}|\d{0,2})$/)
returns 0.123456789012345678 instead of 0.123456789012345
but
format(0.123456789012345,/^-?(\d*\.?\d{0,2}).*/)
returns number formatted to 2 deimal points as expected.
Let me try to explain what's going on.
For the given input 0.12345678901234567890 and the regex /^\d{1,13}(\.\d{1,2}|\d{0,2})$/, let's go step by step and see what's happening.
^\d{1,13} Does indeed match the start of the string 0
(\. Now you've opened a new group, and it does match .
\d{1,2} It does find the digits 1 and 2
|\d{0,2} So this part is skipped
) So this is the end of your capture group.
$ This indicates the end of the string, but it won't match, because you've still got 345678901234567890 remaining.
Javascript returns the whole string because the match failed in the end.
Let's try removing $ at the end, to become /^\d{1,13}(\.\d{1,2}|\d{0,2})/
You'd get back ".12345678901234567890". This generates a couple of questions.
Why did the preceding 0 get removed?
Because it was not part of your matching group, enclosed with ().
Why did we not get only two decimal places, i.e. .12?
Remember that you're doing a replace. Which means that by default, the original string will be kept in place, only the parts that match will get replaced. Since 345678901234567890 was not part of the match, it was left intact. The only part that matched was 0.12.
Answer to title question: your function doesn't replace, because there's nothing to replace - the regex doesn't match anything in the string. csb's answer explains that in all details.
But that's perhaps not the answer you really need.
Now, it seems like you have an XY problem. You ask why your call to .replace() doesn't work, but .replace() is definitely not a function you should use. Role of .replace() is replacing parts of string, while you actually want to create a different string. Moreover, in the comments you suggest that your formatting is not only for presenting data to user, but you also intend to use it in some further computation. You also mention cryptocurriencies.
Let's cope with these problems one-by-one.
What to do instead of replace?
Well, just produce the string you need instead of replacing something in the string you don't like. There are some edge cases. Instead of writing all-in-one regex, just handle them one-by-one.
The following code is definitely not best possible, but it's main aim is to be simple and show exactly what is going on.
function format(n) {
const max_significant_digits = 15;
const max_precision = 2;
let digits_before_decimal_point;
if (n < 0) {
// Don't count minus sign.
digits_before_decimal_point = n.toFixed(0).length - 1;
} else {
digits_before_decimal_point = n.toFixed(0).length;
}
if (digits_before_decimal_point > max_significant_digits) {
throw new Error('No good representation for this number');
}
const available_significant_digits_for_precision =
Math.max(0, max_significant_digits - digits_before_decimal_point);
const effective_max_precision =
Math.min(max_precision, available_significant_digits_for_precision);
const with_trailing_zeroes = n.toFixed(effective_max_precision);
// I want to keep the string and change just matching part,
// so here .replace() is a proper method to use.
const withouth_trailing_zeroes = with_trailing_zeroes.replace(/\.?0*$/, '');
return withouth_trailing_zeroes;
}
So, you got the number formatted the way you want. What now?
What can you use this string for?
Well, you can display it to the user. And that's mostly it. The value was rounded to (1) represent it in a different base and (2) fit in limited precision, so it's pretty much useless for any computation. And, BTW, why would you convert it to String in the first place, if what you want is a number?
Was the value you are trying to print ever useful in the first place?
Well, that's the most serious question here. Because, you know, floating point numbers are tricky. And they are absolutely abysmal for representing money. So, most likely the number you are trying to format is already a wrong number.
What to use instead?
Fixed-point arithmetic is the most obvious answer. Works most of the time. However, it's pretty tricky in JS, where number may slip into floating-point representation almost any time. So, it's better to use decimal arithmetic library. Optionally, switch to a language that has built-in bignums and decimals, like Python.
I'm working on a project where I need to deal with javacsript frameworks for work. We have a parser that reads through them, but errors on lines with .. such as
1..toPrecision()
or
24..map(function(t){return 7..map(function(a){return e[a][t]})
It doesn't seem to understand the "..", and I don't either. Why is this valid javascript? How does mapping on a single number work? Eventually someone will fix the parser, but I'm looking for a temporary fix as to how I can edit the minified .js file to work. Is there another way to write something like 24..map()?
It's kind of a funny situation. Numbers can have a value after the decimal point, right?
console.log(1.2345); // for example
Well, it's also possible to write a number with a decimal point without any numbers following it.
console.log(5.);
So the first dot is the decimal point. The second is the property accessor.
console.log(5. .toString());
// ^ decimal point ^ property accessor
The specification defines decimal literals as:
DecimalIntegerLiteral . DecimalDigits opt ExponentPart opt
where opt means optional.
The first . is the decimal separator character. 1. is a number.
The second . is the object property accessor. someNumber.toPrecision is a function.
Another way to write it would be to write the number with more significant figures:
1.0.toPrecision()
I have read some thousand comma separator JavaScript question/answer but found it hard to apply it in practice. For example I have the variable
x = 10023871234981029898198264897123897.231241235
How will I separate it in thousands with commas? I want a function that not only works with that number of digits but more. Regardless of the number of digits the function I need has to separate the number in commas and leaving the digits after the decimal point as it is, Can anyone help? It has to work on number and turn it into string.
First of all, for such huge numbers you should use string format:
var x = "10023871234981029898198264897123897.231241235";
Otherwise, JavaScript will automatically convert it to exponential notation, i.e. 1.002387123498103e+34.
Then, according to the question about money formatting, you can use the following code:
x.replace(/(\d)(?=(\d{3})+\.)/g, "$1,");
It will result in: "10,023,871,234,981,029,898,198,264,897,123,897.231241235".
alert('g' - 'a') is returning Not a Number. ('NAN').
But I expect, to get the difference between ascii as alert(103-97) => alert(6). Hence 6 to be output.
In C, int i = 'g' - 'a', will give i = 6.
How to achieve this subtraction of 2 characters in javascript? (easily without much effort as below)
alert("g".charCodeAt(0) - "a".charCodeAt(0)) is giving 6.
Application : I am using this in chess program.
The only practicable way to do as you want is the way you've already suggested:
alert('g'.charCodeAt(0) - 'a'.charCodeAt(0));
As you know, this will retrieve the ASCII character code from 0th element of the string in each case, and subtract the second from the first.
Unfortunately this is the only way to retrieve the ASCII code of a given character, though using a function would be somewhat simpler, though given the brevity/simplicity of the charCodeAt() solution not all that much so.
References:
String.charCodeAt().
JavaScript doesn't treat characters as numbers; they are single-character strings instead. So the subtract operator will be calculating Number('g') - Number('a').
You should do 'g'.charCodeAt(0) - 'a'.charCodeAt(0) (there is no better way, but you can wrap it in a function)
You can write yourself a custom function. Something like this:
function asciiDif(a,b) {
return a.charCodeAt(0) - b.charCodeAt(0);
}
And then:
alert(asciiDif('g','a'));
Why the first line below gives error although the second and third lines work fine?
1.toString(); // SyntaxError
(1).toString(); // OK
1['toString'](); // OK
The . presents ambiguity. Is it a decimal, or a property accessor?
The interpreter sees it as a decimal, so you can use .. to allow both the decimal, then the property syntax.
1..toString();
Or use one of the other ways you show to resolve the ambiguity.
The parser is trying to treat 1. as the start of a floating-point literal -- only toString turns it into an invalid number.
Compare with:
1.0.toString()
In (1).toString(), (1) forces it to evaluate before .toString() so it works.
In 1.toString(), 1 is not a valid identifier so it does not work.
In Javascript, using the dot (.) can be interpreted in one of two ways:
As a Property Accessor (e.g., var prop = myObject.prop;).
As part of a Floating-point Literal (e.g. var num = 1.5;).
In the above case, the leading 1. in 1.toString() is interpreted as a floating point number, hence the error:
SyntaxError: identifier starts immediately after numeric literal (learn more)
This is the same error you get if you try and declare a variable that starts with a number: var 1person = 'john';
To prevent the interpreter from seeing the 1. as a decimal and instead see it as accessing a property on our literal 1, there are several ways to accomplish this:
// Via white-space after the numeric literal
1 .toString();
1
.toString();
// Via a grouping-operator, aka, parentheses
// #see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Grouping_operator
(1).toString();
// Via an additional dot. Made clearer with parentheses as `(1.).toString()`
1..toString();
// Via an explicit fractional part (because `1. === 1.0`)
1.0.toString();
// Via bracket notation
1['toString']();
1.['toString']();
1.0['toString']();