locked
Precision of Double Number Type RRS feed

  • 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,
    double-precision binary floating-point 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


    Monday, July 27, 2020 2:58 AM

All replies

  • Hi,
    double-precision binary floating-point 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


    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, Tricks

    Monday, 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:

    Floating-Point 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