Answered by:
Precision of Double Number Type
Question

I am trying to emulate the Nilakantha algorithm to calculate Pi. The formula is:
3 + 4/2×3×4 − 4/4×5×6 + 4/6×7×8 − 4/8×9×10, on and om as long as you can.The issue I am having is that the value stops increasing after not too many iterations and there seems to a discrepancy between how Doubles are handled. below is a console app and a button click sub for a forms app. Any ideas as to why the results are different ?
Console App Code: Module Program Sub Main(args As String()) Dim S As Double = CDbl("21,798,337,296,137,393") Dim DT As Double Dim div As Double = S * (S + 1) * (S + 2) DT = 4D / div Console.WriteLine(div.ToString("F")) Console.WriteLine(DT.ToString("N92")) Console.ReadLine() End Sub End Module Results: 10357861630648208103056607550533164991842751610880.00 0.00000000000000000000000000000000000000000000000038618009610828085951635166931754374121291552 Forms Code Button Click: Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim S As Double = CDbl("21,798,337,296,137,393") Dim DT As Double Dim div As Double = S * (S + 1) * (S + 2) DT = 4D / div TxtResults.AppendText(div.ToString("F") & vbNewLine) TxtResults.AppendText(DT.ToString("N92")) End Sub Results: 10357861630648200000000000000000000000000000000000.00 0.00000000000000000000000000000000000000000000000038618009610828100000000000000000000000000000
The code that does the work is here (in a Background Worker)
Private Sub BGWCalcPi_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BGWCalcPi.DoWork Dim ModVal As Long = Iterations \ 100 Dim Pi As Decimal = 3D Dim Numerator As Decimal = 4D Dim Denominator As Decimal = 2D 'Dim Denom As Decimal Dim Result As Decimal For Count As Long = 1 To Iterations Result = Numerator / Denominator ' three lines below are Result /= Denominator + 1 ' to avoid overflows Result /= Denominator + 2 If Count Mod 2 = 1 Then Pi += Result Else Pi = Result End If Denominator += 2D If Count Mod ModVal = 0 Then BGWCalcPi.ReportProgress(CInt((Count * 100) / Iterations)) End If Next PiString = Pi.ToString("N64") End Sub
The result does not change from using 100 million iterations and 100 billion iterations. Using Decimal type gives overflows.
Sunday, July 26, 2020 11:53 PM
Answers

Hi,
doubleprecision binary floatingpoint format has 52 bit for fraction, see here. The conversion of 10 bits into a decimal representation results in 3 decimal places. 52 bits gives approximately 16 decimal places. With double precision you cannot get a better result.Decimal data type allows 28 decimal places, the largest value is +/7.9228162514264337593543950335, see here.

Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, Tricks Edited by Peter Fleischer Monday, July 27, 2020 3:07 AM
 Marked as answer by Devon_Nullman Tuesday, July 28, 2020 2:36 PM
Monday, July 27, 2020 2:58 AM
All replies

Hi,
doubleprecision binary floatingpoint format has 52 bit for fraction, see here. The conversion of 10 bits into a decimal representation results in 3 decimal places. 52 bits gives approximately 16 decimal places. With double precision you cannot get a better result.Decimal data type allows 28 decimal places, the largest value is +/7.9228162514264337593543950335, see here.

Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, Tricks Edited by Peter Fleischer Monday, July 27, 2020 3:07 AM
 Marked as answer by Devon_Nullman Tuesday, July 28, 2020 2:36 PM
Monday, July 27, 2020 2:58 AM 
Did you try your first Console code separately in a .NET Framework and .NET Core applications?Monday, July 27, 2020 8:10 AM

I get different results in Framework 4.8 an Core 3.1. This is a old known "feature" :(
Core 3.1
10357861630648208103056607550533164991842751610880.00
0.00000000000000000000000000000000000000000000000038618009610828085951635166931754374121291552
Framework 4.8
10357861630648200000000000000000000000000000000000.00
0.00000000000000000000000000000000000000000000000038618009610828100000000000000000000000000000

Best Regards / Viele Grüße
Peter Fleischer (former MVP for Developer Technologies)
Homepage, Tipps, TricksMonday, July 27, 2020 9:52 AM 
The Forms snippet is .NET framework and the Console App is.NET Core
Monday, July 27, 2020 2:31 PM 
Hi Devon_Nullman,
The following blog shows the cause of this problem:
FloatingPoint Parsing and Formatting improvements in .NET Core 3.0
If you test with versions prior to .NET Core 3.0, you will get the same results as in .NET framework.
Best Regards,
Xingyu Zhao
MSDN Community Support
Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.Tuesday, July 28, 2020 6:46 AM 
Here is the complete Console App code, using .NET core 3.1.0 {
Imports System Module Program Sub Main(args As String()) Dim SW As New Stopwatch Dim Iterations As Long = 10000000000 Dim ModVal As Integer = Iterations \ 100 Dim Pi As Decimal = 3D Dim Numerator As Decimal = 4D Dim Denominator As Decimal = 2D Dim Result As Decimal Console.WriteLine("Press Enter to start using " & Iterations.ToString("N0") & " Iterations") Console.ReadLine() SW.Reset() SW.Start() For Count As Long = 1 To Iterations Result = Decimal.Divide(Numerator, Denominator) Result = Decimal.Divide(Result, Denominator + 1D) Result = Decimal.Divide(Result, Denominator + 2D) If Count Mod 2 = 1 Then Pi += Result Else Pi = Result End If Denominator += 2D If Count Mod ModVal = 0 Then Console.SetCursorPosition(0, 1) Console.Write((Count * 100) / Iterations & "%  " & Result.ToString("F64")) End If Next SW.Stop() Console.SetCursorPosition(0, 2) Console.WriteLine("Done  took " & SW.Elapsed.ToString) Console.WriteLine(Pi.ToString("F64")) Console.WriteLine("3.14159265358979323846264338327") ' "Real" Pi Console.ReadLine() End Sub End Module
Some results:
Using 10,000,000 Iterations
Done  took 00:00:04.3837711
3.14159265358979323846239327770
3.14159265358979323846264338327 (Real Value of Pi)
Using 100,000,000 Iterations
Done  took 00:00:41.8777270
3.14159265358979323846264303070
3.14159265358979323846264338327 (Real Value of Pi)
Using 1,000,000,000 Iterations
Done  took 00:07:07.61
3.14159265358979323846264328470
3.14159265358979323846264338327 (Real Value of Pi)
Using 10,000,000,000 Iterations
Done  took 01:09:26.57
3.14159265358979323846264328510
3.14159265358979323846264338327 (Real Value of Pi)
To me it seems like (and makes sense) that it would need an absurd number of Iterations just to get one more significant digit match.Tuesday, July 28, 2020 2:36 PM