Assigning BigInt stores wrong number (number+1) - javascript

I want to define a BigInt number in JavaScript. But when I assign it, the wrong number is stored. In fact 1 is added to the number when storing.
let num = BigInt(0b0000111111111111111111111111111111111111111111111111111111111111)
console.log(num) // Output: 1152921504606846976n
console.log(num.toString(2)) // Output: 1000000000000000000000000000000000000000000000000000000000000
So the number stored is 1152921504606846976, but it should be 11529215046068469765. Why is that?

Converting a Number to a BigInt can't create bits that weren't there before.
0b1 (just like 1) is a Number literal, so it creates a Number.
0b1n (just like 1n) is a BigInt literal, so it creates a BigInt.
By writing BigInt(0b1), you're first creating a Number and then converting that to a BigInt. As long as the value is 1, that works just fine; once the value exceeds what you can losslessly store in a Number [1], you'll see that the value of the final BigInt won't match the literal you wrote down. Whether you use binary (0b...), decimal, or hex (0x...) literals doesn't change any of that.
(And just to be extra clear: there's no reason to write BigInt(123n), just like you wouldn't write Number(123). 123n already is a BigInt, so there's nothing to convert.)
A simple non-BigInt way to illustrate what's happening is to enter 12345678901234567890 into your favorite browser's DevTools console: you can specify Number literals of any length you want, but they'll be parsed into an IEEE754 64-bit "double", which has limited precision. Any extra digits in the literal simply can't be stored, though of course each digit's presence affects the magnitude of the number.
[1] Side note: this condition is more subtle than just saying that Number.MAX_SAFE_INTEGER is the threshold, though that constant is related to the situation: any integral number below MAX_SAFE_INTEGER can be stored losslessly, but there are plenty of numbers above MAX_SAFE_INTEGER that can also be represented exactly. Random example: 1e20.

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.

JSON.stringify() converts integers to exponential

I have a record
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1000000000000000000000
],
JSON.stringify() converts it to the form
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1e+21
],
JSON.stringify() writes it accordingly the same way, can this be somehow solved?
JSON.parse doesn't convert it to 1e+21, it converts it to a number that, when converted to string in the usual way, is output as the string "1e+21". But the number is the same number whether you write it as 1000000000000000000000 or 1e+21.
JSON.stringify may output it in either form; both are valid JSON numbers, and both define exactly the same number.
I should note that you need to beware of numbers of that magnitude in JavaScript (or any other language that uses IEEE-754 double-precision floating point numbers [or single-precision ones, actually]). That number is well into the range where even integers may be imprecisely represented. Any number greater than 9,007,199,254,740,992 (Number.MAX_SAFE_INTEGER + 1) may or may not have a precise representation. It happens that 10,000,00,000,000,000,000,000 (your number) does, but for instance, 9,007,199,254,740,993 doesn't, nor do any odd numbers from that point upward. At some point, you get to where only multiples of 4 can be represented; and then later it's only multiple of 8, etc. See this question's answers for more.
If you still need to get 1e+21 as 1000000000000000000000, you can use (1e+21).toLocaleString().split(',').join('')
but actually, you don't need to convert it if you want to use it as a number, because they are absolutely the same.
Instead, you can keep the number as string and use +'1000000000000000000000' or parseInt('1000000000000000000000') when you need to use it as a number.

How to get Power of big number in decimal?

How to get big power of 2 in decimal or
how to convert big exponential value into decimal value.
I want 2 to the power of 128 in decimal not exponential
what I did till now
tofixed(+exponent)
which again given me the same value.
var num = Math.pow(2, 128);
Actual result = 3.402823669209385e+38
expected some decimal value not exponential value.
You could use BigInt, if implemented.
var num = BigInt(2) ** BigInt(128);
console.log(num.toString());
console.log(BigInt(2 ** 128).toString());
3.402823669209385e+38 is a decimal number (in string form, because it's been output as a string). It's in scientific notation, specifically E-notation. It's the number 3.402823669209385 times 100000000000000000000000000000000000000.
If you want a string that isn't in scientific notation, you can use Intl.NumberFormat for that:
console.log(new Intl.NumberFormat().format(Math.pow(2, 128)));
Note: Although that number is well outside the range that JavaScript's number type can represent with precision in general (any integer above Number.MAX_SAFE_INTEGER [9,007,199,254,740,991] may be the result of rounding), it's one of the values that is held precisely, even at that magnitude, because it's a power of 2. But operations on it that would have a true mathematical result that wasn't a power of 2 would almost certainly get rounded.
I think the default power function won't be able to the results you want.
You can refer to the article below to understand how to create an Power function with big number by yourself.
Demo code is not JS but still quite understandable.
↓
Writing power function for large numbers

JavaScript's Built-in Number() method seems to return an incorrect value

I've made a program to convert bases of numbers. I thought that, in the event that there are no characters A-F in the output, it would be convenient to return the number as a Number instead of a String (even though I'd be returning base 2 numbers as regular decimal numbers, whatever).
For some reason, when I converted A3B232 to Binary (which should be 10100001101011001002 [Wolfram|Alpha source]), I got 1010000110101100200. There's a two in there, that isn't binary! The final line of code which returns the value is:
return (toBase <= 10) ? Number(result) : result;
This worked properly for basically all tests, until I decided to use that base-32 number above. I thought my program was doing the division/remainder step incorrect, so I had it print out the result just before it returns the value. It was correctly giving out 1010000110101100100. So, I tested the Number() method directly in Chrome's console. This is what happened:
> Number("1010000110101100100")
> 1010000110101100200
Am I misunderstanding what the Number() method does? Why is it converting a string made up of all Ones and Zeroes to a number with Ones, Zeroes, and Twos?
What you're doing when calling Number("1010000110101100100") is passing a string, which it is trying to turn it into a number, but that number is higher than JavaScript can count, which is 9,007,199,254,740,991 (also known as Number.MAX_SAFE_INTEGER)
Basically, any number over 9,007,199,254,740,991 can't reliably be used because JavaScript uses double-precision floating-point format numbers as specified in IEEE 754
Here's some examples, the following input numbers "should" be the same as the output, but aren't because they've gone over the MAX_SAFE_INTEGER. Some are, but it's not reliable as demonstrated in the Bonus Fun With LARGE Numbers section below.
Number("9007199254740992") // 9007199254740992
Number("9007199254740993") // 9007199254740992
Number("9007199254740994") // 9007199254740994
Number("9007199254740995") // 9007199254740996
Number("9007199254740996") // 9007199254740996
Number("9007199254740997") // 9007199254740996
Number("9007199254740998") // 9007199254740998
Number("9007199254740999") // 9007199254741000
If you still want to convert hex to binary, you need to use parseInt and Number.prototype.toString and keep it as a string.
const hex = "A3B2";
const binary = parseInt(hex, 16).toString(2);
console.log(binary);
Bonus Fun With LARGE Numbers
The following is true!
console.log(9007199254740992 === 9007199254740993);
console.log(1010000110101100200 === 1010000110101100100)
Documentation
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString
#AnonymusSB said is right. i want add few details to it.
So when you Number(value) it internally calls ToNumber to calculate the value. when you supply a string. it have a two step process to change it number.
First it calculate the Mathematical value (MV) of the string.
In second process it rounds up the calculated value.(when you exceed the limit of Math.MAX_SAFE_INTEGER it round of the number).
console.log(Number("9007199254740996"))
console.log(Number("9007199254740997"), "rounded down")
console.log(Number("9007199254740998"))
console.log(Number("9007199254740999") ,"rounded up")
EcmaScript

How is the parseInt in JavaScript defined to handle large "numbers" - is there an ECMA leak? I got a wow here

The problem: JavaScript returns 20160107190308484 for parseInt("20160107190308485")
Not the question: I do not need a workaround; I got one.
Not the question II: I do not need an explanation why this can happen, and how numbers are stored and so on.
How are numbers as input for parseInt defined? Why do I get a wrong result, not a NaN?
I only find in documentation how parseInt handles literals and so on, that only the first numbers are interpreted and ...
As W3C or MDN
Note: Only the first number in the string is returned!
Note: Leading and trailing spaces are allowed.
Note: If the first character cannot be converted to a number, parseInt() returns NaN.
I can't find anything that says "123456789202232334234212312311" is not a "number". And nothing like "a number must be in the range of -BIGINT .. +BIGINT or so. And I could not find a hint for errors thrown.
I just get 20160107190308484 for "20160107190308485" (two browsers tested) and other pairs like:
20160107155044520, 20160107155044522
20160107002720970, 20160107002720967
20160107000953860, 20160107000953859
For me this feels like a hole in ECMA.
Is there a number defined that is allowed as input for parseInt?
For those who are interested in "how came?":
I stumbled over that problem with a small logic, that converts classes and ids into an object holding the information like
class="book240 lang-en" id="book240page2212"
converting to an object like:
{
book: 240
page: 2212
lang: "en"
}
Notice, that INTs are numbers, not strings.
Therefore I have a loop that makes that generic:
for n in some_regexp_check_names
# m hold a Regexp result arg[1] is the interest:
nr = parseInt(m[1]) # Make it a number
#--------- Here the funny fault
if nr > 0 # Check if number & > 0 all my ids are > 0
out_obj[n]=nr # Take the number
else
out_obj[n]=m[1] # Take the string as is
All this is nice and is working with "real strings" and "real (int) ids", and then I had a situation where dynamically semi uuids out of datetime where created: "temp-2016-01-07-19-03-08.485" - and still all fine, but I had the idea to remove all constant chars from this (i.e. temp-.) and BANG, because the string "20160107190308485" gives 20160107190308484 as parseInt.
And it took me only ~3 hours to find that error.
How are numbers as input for parseInt defined?
I think it's pretty clearly defined in the spec (§18.2.5):
Let mathInt be the mathematical integer value that is represented by Z
in radix-R notation, using the letters A-Z and a-z for digits with
values 10 through 35. (However, if R is 10 and Z contains more than 20
significant digits, every significant digit after the 20th may be
replaced by a 0 digit, at the option of the implementation; and if R
is not 2, 4, 8, 10, 16, or 32, then mathInt may be an
implementation-dependent approximation to the mathematical integer
value that is represented by Z in radix-R notation.)
Let number be the Number value for mathInt.
For that last step, the section for the Number type (§6.1.6) specifies:
In this specification, the phrase “the Number value for x” where x
represents an exact nonzero real mathematical quantity (which might
even be an irrational number such as π) means a Number value chosen in
the following manner. Consider the set of all finite values of the
Number type, with −0 removed and with two additional values added to
it that are not representable in the Number type, namely 21024 (which
is +1 × 253 × 2971) and −21024 (which is −1 × 253 × 2971). Choose the
member of this set that is closest in value to x. If two values of the
set are equally close, then the one with an even significand is
chosen; for this purpose, the two extra values 21024 and −21024 are
considered to have even significands. Finally, if 21024 was chosen,
replace it with +∞; if −21024 was chosen, replace it with −∞; if +0
was chosen, replace it with −0 if and only if x is less than zero; any
other chosen value is used unchanged. The result is the Number value
for x. (This procedure corresponds exactly to the behaviour of the
IEEE 754-2008 “round to nearest, ties to even” mode.)
Why do I get a wrong result not a NaN?
You get a rounded result (with the maximum precision available in that range) - and there's nothing wrong with that, your input is a valid number, not NaN.
Although Bergis answer is correct, I want to tell what my real mistake was:
Don't think of parseInt of a function, that parses into an INTeger like known from C as 16, 32 or 64 bit value. It parse the number as long as there are valid digits, so if the number is to big (does not fit exactly into eg 64 bit), you dont get an error, you just get a rounded value like working with float. If you want to be on the safe side: check the result against Number.MAX_SAFE_INTEGER.
The maximum integer value in JavaScript is 2^53 == 9 007 199 254 740 992. This is because Numbers are stored as floating point is a 52 bit mantissa.
Read more: http://web.archive.org/web/20161117214618/http://bjola.ca/coding/largest-integer-in-javascript/

Categories