How to get digits from a BigInt in javascript? - javascript

I am working on problem n°104 of project Euler Problem 104 and would like to do it in javascript.
In order to solve this problem I need to compute large values of the Fibonacci sequence, but the numbers produced by this sequence are too large to be handle by classic Number, so I'm using BigInt supported in the latest versions of javascript.
Once I've got a particular result stored in a BigInt, I need to check it's 10 first, and last digits.
To get the digits from a Number we usually do something like in the code below, but when the number becomes very large, things go wrong:
let number = BigInt(123456789)
console.log(number.toString())
console.log(number.toString()[3]) // Result is fine
let bigNumber = BigInt(1234567891111111111111111111111111111)
console.log(bigNumber.toString())
console.log(bigNumber.toString()[30]) // unpredictable result
It seems like the "toString()" methods is only using the precision of the Number type (2^53 I believe), thus we are quickly losing precision on the last digits of the BigInt number. The problem is I can't find other methods to extract those digits.
Edit :
I need the precision to be perfect because basicaly what i'm doing for example is :
Compute Fibonacci(500) = 280571172992510140037611932413038677189525
Get the 10 last digits of this number : 8677189525 (this is where is lose the precision)
And then to solve my problem I need to check that those 10 last digits contains all the digits from 1 to 9

For big numbers, I think you should add the n suffix:
let number = BigInt(123456789)
console.log(number.toString())
console.log(number.toString()[3]) // Result is fine
let bigNumber = 1234567891111111111111111111111111111n // <-- n suffix, literal syntax
console.log(bigNumber.toString())
console.log(bigNumber.toString()[30]) // result
let bigNumber2 = BigInt('1234567891111111111111111111111111111') // <-- also works as a string, in case you can't use the literal for some reason
console.log(bigNumber2.toString())
console.log(bigNumber2.toString()[30]) // result

Related

Issue with combining large array of numbers into one single number

I am trying to convert an array of numbers into one single number, for example
[1,2,3] to 123.
However, my code can't handle big arrays since it can’t return exact number. Such as
[6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,5,4,3] returns 6145390195186705000
Is there any way that I could properly convert into a single number.I would really appreciate any help.
var integer = 0;
var digits = [1,2,3,4]
//combine array of digits into int
digits.forEach((num,index,self) => {
integer += num * Math.pow(10,self.length-index-1)
});
The biggest integer value javacript can hold is +/- 9007199254740991. Note that the bitwise operators and shift operators operate on 32-bit ints, so in that case, the max safe integer is 2^31-1, or 2147483647.
In my opinion, you can choose one of the following:
store the numbers as strings and manipulate them as numbers; you might have to implement special functions to add/subtract/multiply/divide them (these are classic algorithmic problems)
use the BigInt; BigInts are a new numeric primitive in JavaScript that can represent integers with arbitrary precision. With BigInts, you can safely store and operate on large integers even beyond the safe integer limit. Unfortunately, they work only with Chrome right now. If you want to work with other browsers, you might check this or even this if you work with angularjs or nodejs.
Try the following code in the Chrome's console:
let x = BigInt([6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,5,4,3].join(''));
console.log(x);
This will print 6145390195186705543n. The n suffix marks that it is a big integer.
Cheers!
You can use JavaScript Array join() Method and parse it into integer.
Example:
parseInt([6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5].join(''))
results:
6145390195186705
Edited: Use BigInt instead of parseInt , but it works only on chrome browser.
The largest number possible in Javascript is
+/- 9007199254740991
Use BigInt. Join all numbers as a string and pass it in BigInt global function to convert it into int
var integer = 0;
var digits = [1,2,3,4]
//combine array of digits into int
digits.forEach((num,index,self) => {
integer += num;
});
integer= BigInt(integer);
Note : Works only on Chrome as of now. You can use othee libraries like BigInteger.js or MathJS

Math.sin() Different Precision between Node.js and C#

I have a problem in precision in the last digit after the comma.The javascript code generates one less Digit in compare with the C# code.
Here is the simple Node.js code
var seed = 45;
var x = Math.sin(seed) * 0.5;
console.log(x);//0.4254517622670592
Here is the simple C# code
public String pseudorandom()
{
int seed = 45;
double num = Math.Sin(seed) * (0.5);
return num.ToString("G15");//0.42545176226705922
}
How to achieve the same precision?
The JavaScript Number type is quite complex. It looks like floating point number will probably be like IEEE 754-2008 but some aspects are left to the implementation. See http://www.ecma-international.org/ecma-262/6.0/#sec-number-objects sec 12.7.
There is a note
The output of toFixed may be more precise than toString for some
values because toString only prints enough significant digits to
distinguish the number from adjacent number values. For example,
(1000000000000000128).toString() returns "1000000000000000100", while
(1000000000000000128).toFixed(0) returns "1000000000000000128".
Hence to get full digit accuracy you need something like
seed = 45;
x = Math.sin(seed) * 0.5;
x.toFixed(17);
// on my platform its "0.42545176226705922"
Also, note the specification for how the implementation of sin and cos allow for some variety in the actual algorithm. It's only guaranteed to within +/- 1 ULP.
Using java the printing algorithm is different. Even forcing 17 digits gives the result as 0.42545176226705920.
You can check you are getting the same bit patterns using x.toString(2) and Double.doubleToLongBits(x) in Java.
return num.ToString("G15");//0.42545176226705922
actually returns "0.425451762267059" (no significant digit + 15 decimal places in this example), and not the precision shown in the comment after.
So you would use:
return num.ToString("G16");
to get "0.4254517622670592"
(for your example - where the significant digit is always 0) G16 will be 16 decimal places.

What does the following snippet do with num?

I read some other's code, there is some piece of code below. I am wondering What does the method do with num?
formatNumber: function (num, digit) {
var pow = Math.pow(10, digit || 5);
return Math.round(num * pow) / pow;
}
BTW when I running
formatNum(11.267898, 5), it gave me 11.2679, is this OK?
Essentially, the function returns the number with certain precision. The precision is digit, which is 5 if not provided.
The return part essentially brings that many values (equal to digit) decimal right to left and then discard the rest and finally divides again to get the original value reduces to precision of digit.
Regarding BTW edit -
The value obtained is correct. See details below
When you call formatNum(11.267898, 5), you're asking the number to round to 5 digit precision and your number has 6 digit precision - precision is digits after the dot.
Now when you call num * pow the number becomes 1126789.8 and when you round this number, it rounds to closest integer which is 11.26790. Finally when you divide it by pow (100000), the number becomes 11.2679, discarding last 0 as trailing Zero in precision is pointless.
That is a really poor piece of code.
First, the naming conventions don't match what the code does at all.
The function name formatNumber() suggests that it formats a number. In other words, it ought to produce a string representation of a number, formatted in some way. But the function doesn't do this, it returns another number. That makes no sense. Numbers don't have a format, they are just numbers.
The parameter name digit sounds like it would contain a single digit. But it doesn't. It contains a count of digits that you want to round the number to. When you name things, singular and plural matter!
It gets worse.
As you found, the function doesn't even work. In your example, formatNum(11.267898,5) returns the number 11.2679. Why did it give you four digits when you asked for five? The result you were expecting was 11.26790, wasn't it? Well, of course that is identical to 11.2679, if we're talking about numbers. But what good does that do you when you wanted five digits?
Or to take a ridiculously simple example: formatNumber(1,2). You might expect that to produce 1.00, but it produces 1. Of course that is really the same value, but not formatted the way you want.
Now we go from the ridiculous to the sublime.
JavaScript has always had a built-in function that does exactly what we would expect formatNumber() to do: number.toFixed(digits). This does proper rounding and always returns the number of digits after the decimal point that you ask for. And of course, to be able to do that, it returns a string, not a number.
If we try these examples using .toFixed() they work as expected:
(11.267898).toFixed(5) returns the string "11.26790".
(1).toFixed(2) returns "1.00".
And so on, for just about anything you can throw at it. (It gives up on numbers with magnitude too large and uses exponential notation instead.)
Note that the parentheses around the first number in those examples are just needed to avoid a syntax error; in most cases you'd be using a variable and they would not be required, e.g.
myNumber.toFixed(2)
To summarize, not only does formatNumber() not do what it says and not anything useful, it was never needed in the first place!

Rounding a figure retrieved from html using javascript and jquery

I am having a little problem with rounding numbers which are brought in from html.
For example a value extracted from <input id="salesValue"> using var salesValue = $("salesValue").val() would give me a text value.
So if I did something like var doubleSalesValue = salesValue + salesValue; , it would return the number as a concatenation instead of summation of the two values.
I could use var doubleSalesValue = salesValue * 2.0; which does return the value which is to multiple decimal places. However, if I did want to use the other method, how can I approach the situation.
What methods do you use? I have created a function which I run on each number where I want to restrict the decimal places along with converting the type to number
function round(number, figure){
return Number(Number(number).toFixed(figure));
}
I have to run Number initially to make sure that the value is converted to type number and has the method toFixed, otherwise it would throw an error here. Then I have to round the number again to the number of decimal places as required by the function, and somehow after running the toFixed method the number would sometimes turn to a string.
So, I decided to run the Number function Number(number).toFixed(figure)
Is there anything else or any different paradigm that you follow?
EDIT: I want to know if what I am doing here is conventional or are there better methods for this in general?
If you want to round it to 2 decimals you can simply do this:
var roundedNum = Math.round(parseFloat(originalNum) * 100) / 100;
Regarding your question:
and somehow after running the toFixed method the number would sometimes turn to a string.
I suggest next time read the dox a bit better https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed which says:
Returns
A string representation of number that does not use exponential
notation and has exactly digits digits after the decimal place. The
number is rounded if necessary, and the fractional part is padded with
zeros if necessary so that it has the specified length. If number is
greater than 1e+21, this method simply calls
Number.prototype.toString() and returns a string in exponential
notation.

numbers and toFixed , toPrecision in Javascript?

Regarding the famous issue of 1.01+1.02 which is 2.0300000000000002
one of the workarounds is to use toFixed : e.g.
(1.01+1.02).toFixed(2) --->"2.03"
But I saw a solution with toPrecision
parseFloat((1.01+1.02).toPrecision(10))-->"2.03"
But lets have a look at n in
toFixed(n)
toPrecision(n)
How would I know what is n ?
0.xxxxxxxxxxx
+
0.yyyyyyyyyyyyy
---------------------
0.zzzzzzzzzzzzzzzzzzzzzzzzz
^
|
-----??????------
each number being added can have a different decimal digits...
for example :
1.0002+1.01+1.03333--> 3.0435300000000005
how would I calculate the n here ? what is the best practice for this (specific) issue ?
For addition as in this situation I would check the number of decimal places in each operand.
In the simplest of situations the number of decimal places in the operand with the greatest number of decimal places is the value of n.
Once you have this, use which ever method you like to truncate your value. Then get rid of trailing zeros.
You may encounter trailing zeros in situations such as 1.06 + 1.04, the first step would take you to 1.10 then truncating the zero would give 1.1
In your last example 1.0002+1.01+1.03333 greatest number of decimal places is 5 so you are left with 3.04353 and there are no trailing zeros to truncate.
This returns the expected output:
function add(){
// Initialize output and "length" properties
var length = 0;
var output = 0;
// Loop through all arguments supplied to this function (So: 1,4,6 in case of add(1,4,6);)
for(var i = 0; i < arguments.length; i++){
// If the current argument's length as string is longer than the previous one (or greater than 0 in case of the first argument))
if(arguments[0].toString().length > length){
// Set the current length to the argument's length (+1 is to account for the decimal point taking 1 character.)
length = arguments[0].toString().length +1;
}
// Add the current character to the output with a precision specified by the longest argument.
output = parseFloat((output+arguments[i]).toPrecision(length));
}
// Do whatever you with with the result, here. Usually, you'd 'return output;'
console.log(output);
}
add(); // Returns 0
add(1,2,3); // Returns 6
add(1.01,2.01,3.03); // Returns 6.05
add(1.01,2.0213,3.3333); // Returns 6.3646
add(11.01,2.0213,31.3333); // Returns 44.3646
parseFloat even gets rid of trailing zero's for you.
This function accepts as many numbers as parameters as you wish, then adds these together taking the numbers' string length into account, when adding them. The precision used in the addition is dynamically modified to fit the "currently added" argument's length.
Fiddle
If you're doing calculations, you have a couple of choices:
multiply the numbers by eg 100, to convert to integers, then do the calculations, then convert back again
do the calculations, dont worry about the rounding errors, then round the result at display time
If you're dealing with money/currencies, the first option is probably not a bad option. If you're just doing scientific maths, I would personally not worry about it, and just round the results at display time, eg to 6 significant figures which is the default for my c++ compiler (gcc; not sure if it is in the c++ standards or not, but if you print 1.234567890 in gcc c++, the output is 1.23457, and the problem is avoided)
var a = 216.57421;
a.toPrecision(1); // => '200' because 216 with 1 < 5;
a.toPrecision(2); // => '220' because 216 with 6 >= 5;
a.toFixed(1); // => 216.6 because 7 >= 5;
a.toFixed(2); // => 216.57 because 4 < 5;

Categories