I was doing some command-line math in powershell and ran into to the following curiosity.
# "{0:f64}" -f ([Math]::PI); "{0:f48}" -f ([Math]::PI)
3.1415926535897931159979634685441851615905761718750000000000000000
3.141592653589793115997963468544185161590576171875
# "{0:f64}" -f ([Math]::E); "{0:f51}" -f ([Math]::E)
2.7182818284590450907955982984276488423347473144531250000000000000
2.718281828459045090795598298427648842334747314453125
Why are e
and π
truncated to different decimal lengths (51 vs. 48)?
And why is it not closer aligned to some even number of words or bytes (like 16,32,64)?
Note that although Pi keeps going beyond 3.141592653589793
, those are just fantasy numbers, because the next digits for the true value of Pi are 2384...
, but PowerShell [Math]
gives you 1159...
.
[Math].Pi
is accurate for the first 16 decimal digits, as is [Math].e
.
[Math].pi (64):
3.141592653589793 1159979634685441851615905761718750000000000000000
Actual Pi:
3.141592653589793 2384626433832795028841971693993751058209749445923...
[Math].e (64):
2.718281828459045 0907955982984276488423347473144531250000000000000
Actual e:
2.718281828459045 2353602874713526624977572470936999595749669676277...
Both are the values you get if you turn a string of the first 16 digits into a double
:
(sandbox) PS C:\Users\grismar> "{0:f64}" -f [double]"3.141592653589793"
3.1415926535897931159979634685441851615905761718750000000000000000
(sandbox) PS C:\Users\grismar> "{0:f64}" -f [double]"2.718281828459045"
2.7182818284590450907955982984276488423347473144531250000000000000
So they are in fact accurate for the same number of initial digits and both values cannot be trusted beyond the 16th digit.
The reason why this works out to different lengths of digit strings when converted to a float is because a float has to be defined using bits just like any number, but unlike integers we can't define every real number exactly. You can look up how floating point numbers like a double
are encoded in detail elsewhere, and that will explain the length difference.
User @Daniel points out in the comments that the source code for [Math]
shows e and Pi being defined with more decimals than we see in your example:
public const double E = 2.7182818284590452354;
public const double PI = 3.14159265358979323846;
However, these values are being assigned to double
data type constants, and the double
simply does not have the precision for values with that many decimals to be sufficiently accurately represented.
There is a data type that would allow this, if you really needed greater precision though:
$piDecimal = [decimal]::Parse("3.1415926535897932384626433833")
"{0:f30}" -f $piDecimal
Output:
3.141592653589793238462643383300
You'll find that trying to replace the final 300
with the even more accurate 279
won't work - that's where you run into the limits of decimal
.