According to microsoft documentation the [BigInt]
datatype seems to have no defined maximum value and theoretically can hold an infinitely large number, but I found that after the 28th digit, some weird things start to occur:
PS C:\Users\Neko> [BigInt]9999999999999999999999999999
9999999999999999999999999999
PS C:\Users\Neko> [BigInt]99999999999999999999999999999
99999999999999991433150857216
As you can see, on the first command1, the BigInt
works as intended, but with one more digit, some falloff seems to occur where it translates 99999999999999999999999999999
to 99999999999999991433150857216
however, the prompt throws no error and you can continue to add more digits until the 310th digit
PS C:\Users\Neko> [BigInt]99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
100000000000000001097906362944045541740492309677311846336810682903157585404911491537163328978494688899061249669721172515611590283743140088328307009198146046031271664502933027185697489699588559043338384466165001178426897626212945177628091195786707458122783970171784415105291802893207873272974885715430223118336
PS C:\Users\Neko\> [BigInt]999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
which will throw the error
At line:1 char:318
+ ... 999999999999999999999999999999999999999999999999999999999999999999999
+ ~
The numeric constant 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 is not valid.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : BadNumericConstant
Which I believe is a console issue rather than a BigInt
issue because the error doesn't mention the [BigInt]
datatype unlike numbers too big for other datatypes like
PS C:\Users\Neko> [UInt64]18446744073709551615
18446744073709551615
PS C:\Users\Neko> [UInt64]18446744073709551616
Cannot convert value "18446744073709551616" to type "System.UInt64". Error: "Value was either too large or too small
for a UInt64."
At line:1 char:1
+ [UInt64]18446744073709551616
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastIConvertible
As for C#, System.Numerics.BigInt
will start throwing an error at the 20th digit, 99999999999999999999
when hard coded:
namespace Test
{
class Test
{
static void Main()
{
System.Numerics.BigInteger TestInput;
System.Numerics.BigInteger Test = 99999999999999999999;
System.Console.WriteLine(Test);
}
}
}
When trying to build in Visual Studio I get the error
Integral constant is too large
However, I can enter a bigger number to ReadLine
without causing an error
namespace Test
{
class Test
{
static void Main()
{
System.Numerics.BigInteger TestInput;
TestInput = System.Numerics.BigInteger.Parse(System.Console.ReadLine());
System.Console.WriteLine(TestInput);
}
}
}
Which seems to indeed be infinite. The input
99999999999...
(24720 characters total) works fine2
So what is causing all of this weird activity with [BigInt]
?
1which is 28 digits according to ([Char[]]"$([BigInt]9999999999999999999999999999)").count
2I am too lazy to count the digits and trying to parse the digits into PowerShell causes an error. According to this it is 24720 characters
TLDR: Use [BigInt]::Parse
or 'literal'
syntax prior to Powershell Core 7.0; otherwise use the n
suffix.
double
literalsWhen it comes to un-suffixed literals, Powershell will use the first type the value fits in. The order for integral literals is int
, long
, decimal
and then double
. From the documentation for Powershell 5.1 (bolding mine; this paragraph is the same for Powershell Core):
For an integer literal with no type suffix:
- If the value can be represented by type
[int]
, that is its type.- Otherwise, if the value can be represented by type
[long]
, that is its type.- Otherwise, if the value can be represented by type
[decimal]
, that is its type.- Otherwise, it is represented by type
[double]
.
In your case the value exceeds that of decimal.MaxValue
so your literal is by default a double
literal. That double
value is not exactly representable and is "converted" to the closest representable double.
$h = [double]99999999999999999999999999999
"{0:G29}" -f $h
Outputs
99999999999999991000000000000
Obviously that's not the exact number, just a representation in string form. But it gives you an idea what's going on. Now we take this inexact double
value and we cast it to BigInt
. The original loss in precision is transferred over and compounded upon by the conversion operator. This is what is actually happening in Powershell (note the cast to BigInt
):
$h = [BigInt][double]99999999999999999999999999999
"{0:G}" -f $h
Outputs
99999999999999991433150857216
This is in fact the closest representable double
value. If you could print the exact value of the double
from the first example, this is what it would print. When you add the additional extra digits, you exceed the largest value of a numeric literal, thus the other exception you received.
Unlike Powershell, C# uses integral literals by default which is why you get the exception for a lot fewer digits. Adding the D
suffix in C# will give you a larger range. The following works fine and will be a double
.
var h = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999D;
Adding one more digit will raise the following error:
error CS0594: Floating-point constant is outside the range of type 'double'
Note that in Powershell the D
suffix is used for decimal
literals and not double
. There is not an explicit suffix for double
--it is assumed to be the default.
Back to your original problem, depending on your Powershell version the solution may vary:
[BigInt]::Parse
If you are using Windows Powershell or Powershell Core <= v6.2, one option is to use BigInteger.Parse
:
[bigint]::Parse("99999999999999999999999999999")
Outputs:
99999999999999999999999999999
As pointed out in the comments, another option that works is to enclose the literal in quotes.
[bigint]'99999999999999999999999999999'
Outputs
99999999999999999999999999999
Despite how it looks, this is not shorthand for [bigint]::new([string])
(see below). This is instead a way to ensure that the literal is not treated as a double
but rather as an integral literal with many digits, a so-called "large value literal". See this section of the docs.
N
Integral Suffix (v7.0+)Powershell Core 6.2 introduced many new literal suffixes for integral types such as unsigned, short
, and byte
but did not introduce one for bigint
. That came along in Powershell Core 7.0 via the n
suffix. This means you can now do the following:
99999999999999999999999999999n
Outputs:
99999999999999999999999999999
See the documentation for more information on the suffixes available in Powershell Core.
[BigInt]::new
If you were to try [bigint]::new('literal')
Powershell recognizes that you intend to use the value as a literal. There is in fact no constructor for BigInt
that accepts a string
(we use Parse
for that) nor is there a constructor which accepts another BigInt
. There is however a constructor that takes a double
. Our large-value literal will start as a BigInt
, Powershell will then implicitly convert that to a double
(losing precision) and then pass it to [bigint]::new([double])
as the best match, once again giving an incorrect result:
[bigint]::new('99999999999999999999999999999')
Outputs:
99999999999999991433150857216
Unfortunately C# have not literal for BigInteger. There are two ways to instantiate BigInteger:
BigInteger test = BigInteger.Parse("32439845934875938475398457938457389475983475893475389457839475");
Console.WriteLine(test.ToString());
// output: 32439845934875938475398457938457389475983475893475389457839475
See How PowerShell parses numeric literals
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With