Unexpected behavior adding stringified integer to decimal in js - javascript

I had a strange bug that I just found the source of in my code.
"1" + .88 // 10.88
What's going on here?

When adding a number and a string (whatever their order), the number is converted to a string and then the two are concatenated.
.88.toString()
is
"0.88"
So you get the string
"10.88"
which appears as
10.88
in most contexts (for example in an HTML input).
If you want a specification based analysis, it starts here with
Then the number to string conversion with the leading 0.is described here:
(s=88, k=2, n=0)

Related

Keep trailing or leading zeroes on number

Is it possible to keep trailing or leading zeroes on a number in javascript, without using e.g. a string instead?
const leading = 003; // literal, leading
const trailing = 0.10; // literal, trailing
const parsed = parseFloat('0.100'); // parsed or somehow converted
console.log(leading, trailing, parsed); // desired: 003 0.10 0.100
This question has been regularly asked (and still is), yet I don't have a place I'd feel comfortable linking to (did i miss it?).
Fully analogously would be keeping any other aspect of the representation a number literal was entered as, although asked nowhere near as often:
console.log(0x10); // 16 instead of potentially desired 0x10
console.log(1e1); // 10 instead of potentially desired 1e1
For disambiguation, this is not about the following topics, for some of which I'll add links, as they might be of interest as well:
Padding to a set amount of digits, formatting to some specific string representation, e.g. How can i pad a value with leading zeroes?, How to output numbers with leading zeros in JavaScript?, How to add a trailing zero to a price
Why a certain string representation will be produced for some number by default, e.g. How does JavaScript determine the number of digits to produce when formatting floating-point values?
Floating point precision/accuracy problems, e.g. console.log(0.1 + 0.2) producing 0.30000000000000004, see Is floating point math broken?, and How to deal with floating point number precision in JavaScript?
No. A number stores no information about the representation it was entered as, or parsed from. It only relates to its mathematical value. Perhaps reconsider using a string after all.
If i had to guess, it would be that much of the confusion comes from the thought, that numbers, and their textual representations would either be the same thing, or at least tightly coupled, with some kind of bidirectional binding between them. This is not the case.
The representations like 0.1 and 0.10, which you enter in code, are only used to generate a number. They are convenient names, for what you intend to produce, not the resulting value. In this case, they are names for the same number. It has a lot of other aliases, like 0.100, 1e-1, or 10e-2. In the actual value, there is no contained information, about what or where it came from. The conversion is a one-way street.
When displaying a number as text, by default (Number.prototype.toString), javascript uses an algorithm to construct one of the possible representations from a number. This can only use what's available, the number value, also meaning it will produce the same results for two same numbers. This implies, that 0.1 and 0.10 will produce the same result.
Concerning the number1 value, javascript uses IEEE754-2019 float642. When source code is being evaluated3, and a number literal is encountered, the engine will convert the mathematical value the literal represents to a 64bit value, according to IEEE754-2019. This means any information about the original representation in code is lost4.
There is another problem, which is somewhat unrelated to the main topic. Javascript used to have an octal notation, with a prefix of "0". This means, that 003 is being parsed as an octal, and would throw in strict-mode. Similarly, 010 === 8 (or an error in strict-mode), see Why JavaScript treats a number as octal if it has a leading zero
In conclusion, when trying to keep information about some representation for a number (including leading or trailing zeroes, whether it was written as decimal, hexadecimal, and so on), a number is not a good choice. For how to achieve some specific representation other than the default, which doesn't need access to the originally entered text (e.g. pad to some amount of digits), there are many other questions/articles, some of which were already linked.
[1]: Javascript also has BigInt, but while it uses a different format, the reasoning is completely analogous.
[2]: This is a simplification. Engines are allowed to use other formats internally (and do, e.g. to save space/time), as long as they are guaranteed to behave like an IEEE754-2019 float64 in any regard, when observed from javascript.
[3]: E.g. V8 would convert to bytecode earlier than evaluation, already exchanging the literal. The only relevant thing is, that the information is lost, before we could do anything with it.
[4]: Javascript gives the ability to operate on code itself (e.g. Function.prototype.toString), which i will not discuss here much. Parsing the code yourself, and storing the representation, is an option, but has nothing to do with how number works (you would be operating on code, a string). Also, i don't immediately see any sane reason to do so, over alternatives.

JavaScript JSON.parse string bug - convert value to Infinity

Can anyone explain me this strange behavior in JSON.parse() function in Javascript?
When calling it with string, it should raise an error.
e.g
JSON.parse("5ffc58ed1662010012d45b30");
result with:
VM230:1 Uncaught SyntaxError: Unexpected token f in JSON at position 1
at JSON.parse (<anonymous>)
at <anonymous>:1:6
...
BUT!!
When I call it with this specific value:
JSON.parse("60000528880e130012727947");
It return Infinity??? Why??? How this possible? What so special in this string?
Is this because this string is entirely consisting numbers and e in the middle? so JSON.parse thinks it's a kind of float?
JSON is a text representation of same data, usually a structure like an array or an object but a primitive value like a string or a number can also be represented as JSON without problems.
Being a text, in the source code it is represented as a JavaScript string.
The line:
JSON.parse("60000528880e130012727947");
can be as well JSON.parse(x), where x is a variable that contains the JSON.
The JSON in the example above is exactly this: 60000528880e130012727947 (there are no quotes around it, the quotes are the way a text is represented in the JavaScript source code). It is the text representation of a real number, 60,000,528,880 * 10^130,012,727,947, to be more precise.
JavaScript uses double-precision 64-bit binary format IEEE 754 to represent the number. The largest value a Number can hold is about 1.8×10^308 which is very much for most practical purposes. However, it is a small value compared to the value you have represented as JSON.
Anyway, no matter how large it is, since the value stored as JSON is larger than the greatest value that can be represented using the 64-bit double-precision format, Infinity is used instead.
Regarding the other example, 5ffc58ed1662010012d45b30 is not a valid representation of a number, therefore the JSON parser throws an error when it reaches the first f character at index 1.
All in all, JSON.parse() works fine, your input is not always valid JSON.
Basically it is considering the second string as a big number

What format is this object's key stored in? %00*%00_data

I have an object with the following key:
*_data
However, the characters are encoded in a non-standard format that is causing my code some problems.
I saved the value of the key to a cookie, and found the following:
%00*%00_data
However, when comparing this string (using == not ===) with the value of the key, they are not equal.
Currently I am storing the key's value into a variable and using that variable as the key. However, I'm extremely curious as to what string I could compare the actual key with that would result in true.
Any help with this mystery would be greatly appreciated.
%00 is the URI-encoded UTF-8 representation of the character NUL, representing the null character. I imagine this is used here to differentiate between the actual * character and a * character which may be replaced by some library you're using.
We can get from %00*%00_data to *_data by using JavaScript's decideURIComponent() method, which, as the name suggests, decodes the URI-encoded character:
decodeURIComponent("%00*%00_data");
Based on your comments, it seems that the key variable you're comparing against is actually this string of length 8. I mention this, because the string *_data you've included in your question is of length 6, as it doesn't include the two null characters.

JSON.parse parses / converts big numbers incorrectly

My problem is really simple but I'm not sure if there's a "native" solution using JSON.parse.
I receive this string from an API :
{ "key" : -922271061845347495 }
When I'm using JSON.parse on this string, it turns into this object:
{ "key" : -922271061845347500 }
As you can see, the parsing stops when the number is too long (you can check this behavior here). It has only 15 exact digits, the last one is rounded and those after are set to 0. Is there a "native" solution to keep the exact value ? (it's an ID so I can't round it)
I know I can use regex to solve this problem but I'd prefer to use a "native" method if it exists.
Your assumption that the parsing stops after certain digits is incorrect.
It says here:
In JavaScript all numbers are floating-point numbers. JavaScript uses
the standard 8 byte IEEE floating-point numeric format, which means
the range is from:
±1.7976931348623157 x 10308 - very large, and ±5 x 10-324 - very small.
As JavaScript uses floating-point numbers the accuracy is only assured
for integers between: -9007199254740992 (-253) and 9007199254740992
(253)
You number lies outside the "accurate" range hence it is converted to the nearest representation of the JavaScript number. Any attempt to evaluate this number (using JSON.parse, eval, parseInt) will cause data loss. I therefore recommend that you pass the key as a string. If you do not control the API, file a feature request.
The number is too big to be parsed correctly.
One solution is:
Preprocessing your string from API to convert it into string before parsing.
Preform normal parsing
Optionally, you could convert it back into number for your own purpose.
Here is the RegExp to convert all numbers in your string (proceeded with :) into strings:
// convert all number fields into strings to maintain precision
// : 922271061845347495, => : "922271061845347495",
stringFromApi = stringFromApi.replace(/:\s*(-?\d+),/g, ': "$1",');
Regex explanation:
\s* any number of spaces
-? one or zero '-' symbols (negative number support)
\d+ one or more digits
(...) will be put in the $1 variable

why do convert numbers to string in Javascript using tostring method?

I was wondering why do people have to convert numbers to string. What are the practical uses for that kind of conversion?
Similarly why do developers use parseInt or parseFloat to convert a string to a number.
thanks
The variable’s data type is the JavaScript scripting engine’s interpretation of the type of data that variable is currently holding. A string variable holds a string; a number variable holds a number value, and so on. However, unlike many other languages, in JavaScript, the same variable can hold different types of data, all within the same application. This is a concept known by the terms loose typing and dynamic typing, both of which mean that a JavaScript variable can hold different data types at different times depending on context.
With a loosely typed language, you don’t have to declare ahead of time that a variable will be a string or a number or a boolean, as the data type is actually determined while the application is being processed. If you start out with a string variable and then want to use it as a number, that’s perfectly fine, as long as the string actually contains something that resembles a number and not something such as an email address. If you later want to treat it as a string again, that’s fine, too.
The forgiving nature of loose typing can end up generating problems. If you try to add two numbers together, but the JavaScript engine interprets the variable holding one of them as a string data type, you end up with an odd string, rather than the sum you were expecting. Context is everything when it comes to variables and data types with JavaScript.
Using parseInt and parseFloat is important if you want to do arithmetic operations on a number which is in string form. For example
"42" + 1 === "421"
parseInt("42") + 1 === 43;
The reverse is true when you want to do string operations on values which are currently a number.
42 + 1 === 43
(42 + "") + 1 === 421
Why one would want to do the former or latter though is very scenario specific. I'd wager the case of converting strings to numbers for arithmetic operations is the more prominent case though.
An example of when converting numbers to strings is useful is when you want to format the number a certain way, perhaps like a currency (1234.56 -> $1,234.56).
The converse is useful when you want to do arithmetic on strings the represent numbers. Say you have a text box were you allow the user to input a number. The value of that text box will be a string, but you need it as a number to do some arithmetic with it, so you would use parseInt and parseFloat.
string -> number:
Think about simple number validation using JS. if you can convert a string into a number, then you can validate that number before posting to a number, or for use in an arithmetic operation.
number -> string:
String concatenation mainly and display purposes. The language will most often use implicit conversion to convert the number into a string anyway, such as:
1 + " new answer has been posted"
Do remember, Javascript is a loosely typed language. This can hide a lot of implicit type-casting that is occurring.

Categories