Double Структура

Определение

Представляет число двойной точности с плавающей запятой.

public value class double : IComparable, IComparable<double>, IConvertible, IEquatable<double>, IFormattable
public value class double : IComparable, IComparable<double>, IConvertible, IEquatable<double>, ISpanFormattable
public value class double : IComparable, IConvertible, IFormattable
public value class double : IComparable, IComparable<double>, IEquatable<double>, IFormattable
public struct Double : IComparable, IComparable<double>, IConvertible, IEquatable<double>, IFormattable
public struct Double : IComparable, IComparable<double>, IConvertible, IEquatable<double>, ISpanFormattable
[System.Serializable]
public struct Double : IComparable, IConvertible, IFormattable
[System.Serializable]
[System.Runtime.InteropServices.ComVisible(true)]
public struct Double : IComparable, IComparable<double>, IConvertible, IEquatable<double>, IFormattable
public struct Double : IComparable, IComparable<double>, IEquatable<double>, IFormattable
type double = struct
    interface IConvertible
    interface IFormattable
type double = struct
    interface IConvertible
    interface ISpanFormattable
    interface IFormattable
[<System.Serializable>]
type double = struct
    interface IFormattable
    interface IConvertible
[<System.Serializable>]
[<System.Runtime.InteropServices.ComVisible(true)>]
type double = struct
    interface IFormattable
    interface IConvertible
type double = struct
    interface IFormattable
Public Structure Double
Implements IComparable, IComparable(Of Double), IConvertible, IEquatable(Of Double), IFormattable
Public Structure Double
Implements IComparable, IComparable(Of Double), IConvertible, IEquatable(Of Double), ISpanFormattable
Public Structure Double
Implements IComparable, IConvertible, IFormattable
Public Structure Double
Implements IComparable, IComparable(Of Double), IEquatable(Of Double), IFormattable
Наследование
Double
Атрибуты
Реализации

Примеры

В следующем примере кода показано использование Double :

// The Temperature class stores the temperature as a Double
// and delegates most of the functionality to the Double 
// implementation.
public ref class Temperature: public IComparable, public IFormattable
{
   // IComparable.CompareTo implementation.
public:
   virtual int CompareTo( Object^ obj )
   {
      if (obj == nullptr) return 1;
      
      if (dynamic_cast<Temperature^>(obj) )
      {
         Temperature^ temp = (Temperature^)(obj);
         return m_value.CompareTo( temp->m_value );
      }
      throw gcnew ArgumentException( "object is not a Temperature" );
   }

   // IFormattable.ToString implementation.
   virtual String^ ToString( String^ format, IFormatProvider^ provider )
   {
      if ( format != nullptr )
      {
         if ( format->Equals( "F" ) )
         {
            return String::Format( "{0}'F", this->Value.ToString() );
         }

         if ( format->Equals( "C" ) )
         {
            return String::Format( "{0}'C", this->Celsius.ToString() );
         }
      }
      return m_value.ToString( format, provider );
   }

   // Parses the temperature from a string in the form
   // [ws][sign]digits['F|'C][ws]
   static Temperature^ Parse( String^ s, NumberStyles styles, IFormatProvider^ provider )
   {
      Temperature^ temp = gcnew Temperature;

      if ( s->TrimEnd(nullptr)->EndsWith( "'F" ) )
      {
         temp->Value = Double::Parse( s->Remove( s->LastIndexOf( '\'' ), 2 ), styles, provider );
      }
      else
      if ( s->TrimEnd(nullptr)->EndsWith( "'C" ) )
      {
         temp->Celsius = Double::Parse( s->Remove( s->LastIndexOf( '\'' ), 2 ), styles, provider );
      }
      else
      {
         temp->Value = Double::Parse( s, styles, provider );
      }
      return temp;
   }

protected:
   double m_value;

public:
   property double Value 
   {
      double get()
      {
         return m_value;
      }

      void set( double value )
      {
         m_value = value;
      }
   }

   property double Celsius 
   {
      double get()
      {
         return (m_value - 32.0) / 1.8;
      }

      void set( double value )
      {
         m_value = 1.8 * value + 32.0;
      }
   }
};
// The Temperature class stores the temperature as a Double
// and delegates most of the functionality to the Double
// implementation.
public class Temperature : IComparable, IFormattable
{
    // IComparable.CompareTo implementation.
    public int CompareTo(object obj) {
        if (obj == null) return 1;

        Temperature temp = obj as Temperature;
        if (obj != null)
            return m_value.CompareTo(temp.m_value);
        else
            throw new ArgumentException("object is not a Temperature");	
    }

    // IFormattable.ToString implementation.
    public string ToString(string format, IFormatProvider provider) {
        if( format != null ) {
            if( format.Equals("F") ) {
                return String.Format("{0}'F", this.Value.ToString());
            }
            if( format.Equals("C") ) {
                return String.Format("{0}'C", this.Celsius.ToString());
            }
        }

        return m_value.ToString(format, provider);
    }

    // Parses the temperature from a string in the form
    // [ws][sign]digits['F|'C][ws]
    public static Temperature Parse(string s, NumberStyles styles, IFormatProvider provider) {
        Temperature temp = new Temperature();

        if( s.TrimEnd(null).EndsWith("'F") ) {
            temp.Value = Double.Parse( s.Remove(s.LastIndexOf('\''), 2), styles, provider);
        }
        else if( s.TrimEnd(null).EndsWith("'C") ) {
            temp.Celsius = Double.Parse( s.Remove(s.LastIndexOf('\''), 2), styles, provider);
        }
        else {
            temp.Value = Double.Parse(s, styles, provider);
        }

        return temp;
    }

    // The value holder
    protected double m_value;

    public double Value {
        get {
            return m_value;
        }
        set {
            m_value = value;
        }
    }

    public double Celsius {
        get {
            return (m_value-32.0)/1.8;
        }
        set {
            m_value = 1.8*value+32.0;
        }
    }
}
' Temperature class stores the value as Double
' and delegates most of the functionality 
' to the Double implementation.
Public Class Temperature
    Implements IComparable, IFormattable

    Public Overloads Function CompareTo(ByVal obj As Object) As Integer _
        Implements IComparable.CompareTo

        If TypeOf obj Is Temperature Then
            Dim temp As Temperature = CType(obj, Temperature)

            Return m_value.CompareTo(temp.m_value)
        End If

        Throw New ArgumentException("object is not a Temperature")
    End Function

    Public Overloads Function ToString(ByVal format As String, ByVal provider As IFormatProvider) As String _
        Implements IFormattable.ToString

        If Not (format Is Nothing) Then
            If format.Equals("F") Then
                Return [String].Format("{0}'F", Me.Value.ToString())
            End If
            If format.Equals("C") Then
                Return [String].Format("{0}'C", Me.Celsius.ToString())
            End If
        End If

        Return m_value.ToString(format, provider)
    End Function

    ' Parses the temperature from a string in form
    ' [ws][sign]digits['F|'C][ws]
    Public Shared Function Parse(ByVal s As String, ByVal styles As NumberStyles, ByVal provider As IFormatProvider) As Temperature
        Dim temp As New Temperature()

        If s.TrimEnd(Nothing).EndsWith("'F") Then
            temp.Value = Double.Parse(s.Remove(s.LastIndexOf("'"c), 2), styles, provider)
        Else
            If s.TrimEnd(Nothing).EndsWith("'C") Then
                temp.Celsius = Double.Parse(s.Remove(s.LastIndexOf("'"c), 2), styles, provider)
            Else
                temp.Value = Double.Parse(s, styles, provider)
            End If
        End If
        Return temp
    End Function

    ' The value holder
    Protected m_value As Double

    Public Property Value() As Double
        Get
            Return m_value
        End Get
        Set(ByVal Value As Double)
            m_value = Value
        End Set
    End Property

    Public Property Celsius() As Double
        Get
            Return (m_value - 32) / 1.8
        End Get
        Set(ByVal Value As Double)
            m_value = Value * 1.8 + 32
        End Set
    End Property
End Class

Комментарии

DoubleТип значения представляет 64-разрядное число двойной точности со значениями от отрицательного 1.79769313486232 E308 до положительного 1.79769313486232 E308, а также положительного или отрицательного нуля,, PositiveInfinity NegativeInfinity , а не числа ( NaN ). Он предназначен для представления очень больших значений (например, расстояния между планеты или ГАЛАКСИЕС) или чрезвычайно малым (например, молекулярное масса вещества в килограммах) и часто являются неточными (например, расстояние от земли до другой солнечной системы). DoubleТип соответствует стандарту IEC 60559:1989 (IEEE 754) для бинарной арифметики с плавающей запятой.

В этом разделе:

Floating-Point представление и точность

DoubleТип данных хранит значения с плавающей запятой двойной точности в 64-разрядном двоичном формате, как показано в следующей таблице.

Отделение Bits
Значащим или мантисса 0-51
Показатель степени 52-62
Знак (0 = положительный, 1 = отрицательный) 63

Точно так же, как десятичные дроби не могут точно представлять некоторые дробные значения (например, 1/3 или Math.PI ), двоичные дроби не могут представлять некоторые дробные значения. Например, 1/10, которая точно представляется в виде десятичной дроби, представляется в виде. 001100110011 в виде двоичной дроби с шаблоном «0011», повторяющимся до бесконечности. В этом случае значение с плавающей запятой обеспечивает неточное представление числа, которое оно представляет. Выполнение дополнительных математических операций с исходным значением с плавающей запятой часто приводит к увеличению нехватки точности. Например, если сравнить результат умножения на 10, а затем увеличить значение от 0,1 до. 1 9 раз, мы видим, что это сложение, так как оно включало восемь дополнительных операций, создало менее точный результат. Обратите внимание, что это различие очевидно только в том случае, если мы отображаем два Double значения с помощью строки стандартного числового форматаR, которая при необходимости отображает все 17 знаков точности, поддерживаемые Double типом.

using System;

public class Example
{
   public static void Main()
   {
      Double value = .1;
      Double result1 = value * 10;
      Double result2 = 0;
      for (int ctr = 1; ctr <= 10; ctr++)
         result2 += value;

      Console.WriteLine(".1 * 10:           {0:R}", result1);
      Console.WriteLine(".1 Added 10 times: {0:R}", result2);
   }
}
// The example displays the following output:
//       .1 * 10:           1
//       .1 Added 10 times: 0.99999999999999989
Module Example
   Public Sub Main()
      Dim value As Double = .1
      Dim result1 As Double = value * 10
      Dim result2 As Double
      For ctr As Integer = 1 To 10
         result2 += value
      Next
      Console.WriteLine(".1 * 10:           {0:R}", result1)
      Console.WriteLine(".1 Added 10 times: {0:R}", result2)
   End Sub
End Module
' The example displays the following output:
'       .1 * 10:           1
'       .1 Added 10 times: 0.99999999999999989

Поскольку некоторые числа не могут быть представлены в виде дробных двоичных значений, числа с плавающей запятой могут быть приблизительными только вещественными числами.

Все числа с плавающей запятой также имеют ограниченное количество значащих цифр, что также определяет, насколько точное значение числа с плавающей запятой приблизительно равно вещественному числу. DoubleЗначение имеет длину до 15 десятичных разрядов, хотя для внутренних целей поддерживается не более 17 цифр. Это означает, что некоторые операции с плавающей точкой могут не иметь точности изменять значение с плавающей запятой. Ниже приведен пример. Он определяет очень большое значение с плавающей запятой, а затем добавляет Double.Epsilon к нему продукт и один квадриллион. Однако продукт слишком мал для изменения исходного значения с плавающей запятой. Его минимальная значимая цифра состоит из тысяч, в то время как наиболее значимая цифра в продукте — 10-309.

using System;

public class Example
{
   public static void Main()
   {
      Double value = 123456789012.34567;
      Double additional = Double.Epsilon * 1e15;
      Console.WriteLine("{0} + {1} = {2}", value, additional,
                                           value + additional);
   }
}
// The example displays the following output:
//    123456789012.346 + 4.94065645841247E-309 = 123456789012.346
Module Example
   Public Sub Main()
      Dim value As Double = 123456789012.34567
      Dim additional As Double = Double.Epsilon * 1e15
      Console.WriteLine("{0} + {1} = {2}", value, additional, 
                                           value + additional)
   End Sub
End Module
' The example displays the following output:
'   123456789012.346 + 4.94065645841247E-309 = 123456789012.346

Ограниченная точность числа с плавающей запятой имеет несколько последствий:

  • Два числа с плавающей запятой, которые могут казаться равными при определенной точности, на самом деле отличаются, поскольку их менее значащие цифры различаются. В следующем примере ряд чисел добавляется вместе, а их итог сравнивается с ожидаемым итогом. Хотя два значения выглядят одинаково, вызов Equals метода указывает, что они не являются.

    using System;
    
    public class Example
    {
       public static void Main()
       {
          Double[] values = { 10.0, 2.88, 2.88, 2.88, 9.0 };
          Double result = 27.64;
          Double total = 0;
          foreach (var value in values)
             total += value;
    
          if (total.Equals(result))
             Console.WriteLine("The sum of the values equals the total.");
          else
             Console.WriteLine("The sum of the values ({0}) does not equal the total ({1}).",
                               total, result);
       }
    }
    // The example displays the following output:
    //      The sum of the values (36.64) does not equal the total (36.64).
    //
    // If the index items in the Console.WriteLine statement are changed to {0:R},
    // the example displays the following output:
    //       The sum of the values (27.639999999999997) does not equal the total (27.64).
    
    Module Example
       Public Sub Main()
          Dim values() As Double = { 10.0, 2.88, 2.88, 2.88, 9.0 }
          Dim result As Double = 27.64
          Dim total As Double
          For Each value In values
             total += value
          Next
          If total.Equals(result) Then
             Console.WriteLine("The sum of the values equals the total.")
          Else
             Console.WriteLine("The sum of the values ({0}) does not equal the total ({1}).",
                               total, result) 
          End If     
       End Sub
    End Module
    ' The example displays the following output:
    '      The sum of the values (36.64) does not equal the total (36.64).   
    '
    ' If the index items in the Console.WriteLine statement are changed to {0:R},
    ' the example displays the following output:
    '       The sum of the values (27.639999999999997) does not equal the total (27.64).
    

    Если изменить элементы форматирования в Console.WriteLine(String, Object, Object) инструкции с {0} и {1} на {0:R} и, {1:R} чтобы отобразить все значащие цифры двух Double значений, то ясно, что эти два значения не равны из-за потери точности во время операций сложения. В этом случае проблему можно устранить, вызвав Math.Round(Double, Int32) метод, чтобы округлить Double значения до нужной точности перед выполнением сравнения.

  • Математическая операция OR, использующая число с плавающей запятой, может не дать одинакового результата, если используется десятичное число, так как число двоичных с плавающей запятой может не совпадать с десятичным числом. Предыдущий пример демонстрирует это, отображая результат умножения десята на 10 и добавляя. 1 раз.

    Если точность числовых операций с дробными значениями важна, можно использовать Decimal вместо Double типа. Если точность числовых операций с целочисленными значениями вне диапазона Int64 UInt64 типов или важна, используйте BigInteger тип.

  • Значение может не циклически передавалться, если используется число с плавающей запятой. Значение считается циклическим, если операция преобразует исходное число с плавающей запятой в другую форму, операция обратного преобразования преобразует преобразованную форму обратно в число с плавающей запятой, а окончательное число с плавающей запятой не равно исходному числу с плавающей запятой. Цикл обработки может завершиться ошибкой, поскольку одна или несколько наименьших значащих цифр теряются или изменяются при преобразовании. В следующем примере три Double значения преобразуются в строки и сохраняются в файле. Как видно из выходных данных, несмотря на то, что значения выглядят идентичными, восстановленные значения не равны исходным значениям.

    using System;
    using System.IO;
    
    public class Example
    {
       public static void Main()
       {
          StreamWriter sw = new StreamWriter(@".\Doubles.dat");
          Double[] values = { 2.2/1.01, 1.0/3, Math.PI };
          for (int ctr = 0; ctr < values.Length; ctr++) {
             sw.Write(values[ctr].ToString());
             if (ctr != values.Length - 1)
                sw.Write("|");
          }
          sw.Close();
    
          Double[] restoredValues = new Double[values.Length];
          StreamReader sr = new StreamReader(@".\Doubles.dat");
          string temp = sr.ReadToEnd();
          string[] tempStrings = temp.Split('|');
          for (int ctr = 0; ctr < tempStrings.Length; ctr++)
             restoredValues[ctr] = Double.Parse(tempStrings[ctr]);
    
          for (int ctr = 0; ctr < values.Length; ctr++)
             Console.WriteLine("{0} {2} {1}", values[ctr],
                               restoredValues[ctr],
                               values[ctr].Equals(restoredValues[ctr]) ? "=" : "<>");
       }
    }
    // The example displays the following output:
    //       2.17821782178218 <> 2.17821782178218
    //       0.333333333333333 <> 0.333333333333333
    //       3.14159265358979 <> 3.14159265358979
    
    Imports System.IO
    
    Module Example
       Public Sub Main()
          Dim sw As New StreamWriter(".\Doubles.dat")
          Dim values() As Double = { 2.2/1.01, 1.0/3, Math.PI }
          For ctr As Integer = 0 To values.Length - 1
             sw.Write(values(ctr).ToString())
             If ctr <> values.Length - 1 Then sw.Write("|")
          Next      
          sw.Close()
          
          Dim restoredValues(values.Length - 1) As Double
          Dim sr As New StreamReader(".\Doubles.dat")
          Dim temp As String = sr.ReadToEnd()
          Dim tempStrings() As String = temp.Split("|"c)
          For ctr As Integer = 0 To tempStrings.Length - 1
             restoredValues(ctr) = Double.Parse(tempStrings(ctr))   
          Next 
    
          For ctr As Integer = 0 To values.Length - 1
             Console.WriteLine("{0} {2} {1}", values(ctr), 
                               restoredValues(ctr),
                               If(values(ctr).Equals(restoredValues(ctr)), "=", "<>"))
          Next
       End Sub
    End Module
    ' The example displays the following output:
    '       2.17821782178218 <> 2.17821782178218
    '       0.333333333333333 <> 0.333333333333333
    '       3.14159265358979 <> 3.14159265358979
    

    В этом случае можно успешно выполнить циклический обмен значениями, используя стандартную строку числового формата "G17" для сохранения полной точности Double значений, как показано в следующем примере.

    using System;
    using System.IO;
    
    public class Example
    {
       public static void Main()
       {
          StreamWriter sw = new StreamWriter(@".\Doubles.dat");
          Double[] values = { 2.2/1.01, 1.0/3, Math.PI };
          for (int ctr = 0; ctr < values.Length; ctr++)
             sw.Write("{0:G17}{1}", values[ctr], ctr < values.Length - 1 ? "|" : "" );
    
          sw.Close();
    
          Double[] restoredValues = new Double[values.Length];
          StreamReader sr = new StreamReader(@".\Doubles.dat");
          string temp = sr.ReadToEnd();
          string[] tempStrings = temp.Split('|');
          for (int ctr = 0; ctr < tempStrings.Length; ctr++)
             restoredValues[ctr] = Double.Parse(tempStrings[ctr]);
    
          for (int ctr = 0; ctr < values.Length; ctr++)
             Console.WriteLine("{0} {2} {1}", values[ctr],
                               restoredValues[ctr],
                               values[ctr].Equals(restoredValues[ctr]) ? "=" : "<>");
       }
    }
    // The example displays the following output:
    //       2.17821782178218 = 2.17821782178218
    //       0.333333333333333 = 0.333333333333333
    //       3.14159265358979 = 3.14159265358979
    
    Imports System.IO
    
    Module Example
       Public Sub Main()
          Dim sw As New StreamWriter(".\Doubles.dat")
          Dim values() As Double = { 2.2/1.01, 1.0/3, Math.PI }
          For ctr As Integer = 0 To values.Length - 1
             sw.Write("{0:G17}{1}", values(ctr), 
                      If(ctr < values.Length - 1, "|", ""))
          Next      
          sw.Close()
          
          Dim restoredValues(values.Length - 1) As Double
          Dim sr As New StreamReader(".\Doubles.dat")
          Dim temp As String = sr.ReadToEnd()
          Dim tempStrings() As String = temp.Split("|"c)
          For ctr As Integer = 0 To tempStrings.Length - 1
             restoredValues(ctr) = Double.Parse(tempStrings(ctr))   
          Next 
    
          For ctr As Integer = 0 To values.Length - 1
             Console.WriteLine("{0} {2} {1}", values(ctr), 
                               restoredValues(ctr),
                               If(values(ctr).Equals(restoredValues(ctr)), "=", "<>"))
          Next
       End Sub
    End Module
    ' The example displays the following output:
    '       2.17821782178218 = 2.17821782178218
    '       0.333333333333333 = 0.333333333333333
    '       3.14159265358979 = 3.14159265358979
    

Важно!

При использовании со Double значением описатель формата R в некоторых случаях не может успешно выполнить циклический обмен исходным значением. Чтобы обеспечить Double успешный циклический обмен значениями, используйте описатель формата "G17".

  • Single значения имеют меньшую точность, чем Double значения. SingleЗначение, которое преобразуется в эквивалентный, Double часто не равно Double значению из-за различий в точности. В следующем примере результат идентичных операций деления присваивается Double и Single значению. После Single приведения значения к типу Double , сравнение двух значений показывает, что они не равны.

    using System;
    
    public class Example
    {
       public static void Main()
       {
          Double value1 = 1/3.0;
          Single sValue2 = 1/3.0f;
          Double value2 = (Double) sValue2;
          Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
                                              value1.Equals(value2));
       }
    }
    // The example displays the following output:
    //        0.33333333333333331 = 0.3333333432674408: False
    
    Module Example
       Public Sub Main()
          Dim value1 As Double = 1/3
          Dim sValue2 As Single = 1/3
          Dim value2 As Double = CDbl(sValue2)
          Console.WriteLine("{0} = {1}: {2}", value1, value2, value1.Equals(value2))
       End Sub
    End Module
    ' The example displays the following output:
    '       0.33333333333333331 = 0.3333333432674408: False
    

    Чтобы избежать этой проблемы, используйте либо Double вместо Single типа данных, либо используйте Round метод, чтобы оба значения имели одинаковую точность.

Кроме того, результат арифметических операций и операции присваивания со Double значениями могут немного отличаться от платформы из-за потери точности Double типа. например, результат присваивания литерального Double значения может отличаться в 32-разрядной и 64-разрядной версиях платформа .NET Framework. В следующем примере демонстрируется это различие, когда литеральное значение-4.42330604244772 E-305 и переменная со значением-4.42330604244772 E-305 присваивается Double переменной. Обратите внимание, что Parse(String) в этом случае результат метода не страдает от потери точности.

double value = -4.42330604244772E-305;

double fromLiteral = -4.42330604244772E-305;
double fromVariable = value;
double fromParse = Double.Parse("-4.42330604244772E-305");

Console.WriteLine("Double value from literal: {0,29:R}", fromLiteral);
Console.WriteLine("Double value from variable: {0,28:R}", fromVariable);
Console.WriteLine("Double value from Parse method: {0,24:R}", fromParse);
// On 32-bit versions of the .NET Framework, the output is:
//    Double value from literal:        -4.42330604244772E-305
//    Double value from variable:       -4.42330604244772E-305
//    Double value from Parse method:   -4.42330604244772E-305
//
// On other versions of the .NET Framework, the output is:
//    Double value from literal:      -4.4233060424477198E-305
//    Double value from variable:     -4.4233060424477198E-305
//    Double value from Parse method:   -4.42330604244772E-305
Dim value As Double = -4.42330604244772E-305

Dim fromLiteral As Double = -4.42330604244772E-305
Dim fromVariable As Double = value
Dim fromParse As Double = Double.Parse("-4.42330604244772E-305")

Console.WriteLine("Double value from literal: {0,29:R}", fromLiteral)
Console.WriteLine("Double value from variable: {0,28:R}", fromVariable)
Console.WriteLine("Double value from Parse method: {0,24:R}", fromParse)      
' On 32-bit versions of the .NET Framework, the output is:
'    Double value from literal:        -4.42330604244772E-305
'    Double value from variable:       -4.42330604244772E-305
'    Double value from Parse method:   -4.42330604244772E-305
'
' On other versions of the .NET Framework, the output is:
'    Double value from literal:        -4.4233060424477198E-305
'    Double value from variable:       -4.4233060424477198E-305
'    Double value from Parse method:     -4.42330604244772E-305

Проверка на равенство

Чтобы считаться равными, два Double значения должны представлять одинаковые значения. Однако из-за различий в точности между значениями или из-за потери точности на одно или оба значения значения с плавающей запятой, которые должны быть одинаковыми, часто становятся неравными из-за различий в их минимально значащих цифрах. В результате вызовы Equals метода для определения того, равны ли два значения, или вызовы CompareTo метода для определения связи между двумя Double значениями, часто дают непредвиденные результаты. Это очевидно в следующем примере, где два очевидных значения могут Double быть неравными, так как первая имеет 15 разрядов точности, а вторая — 17.

using System;

public class Example
{
   public static void Main()
   {
      double value1 = .333333333333333;
      double value2 = 1.0/3;
      Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2));
   }
}
// The example displays the following output:
//        0.333333333333333 = 0.33333333333333331: False
Module Example
   Public Sub Main()
      Dim value1 As Double = .333333333333333
      Dim value2 As Double = 1/3
      Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2))
   End Sub
End Module
' The example displays the following output:
'       0.333333333333333 = 0.33333333333333331: False

Вычисляемые значения, которые следуют разным путям кода и управляются разными способами, часто не считаются равными. В следующем примере одно Double значение помещается в квадрат, а затем вычисляется квадратный корень для восстановления исходного значения. Вторая Double умножается на 3,51 и в квадрате, прежде чем квадратный корень результата делится на 3,51 для восстановления исходного значения. Несмотря на то, что два значения выглядят одинаковыми, вызов Equals(Double) метода указывает, что они не равны. Использование строки стандартного формата "R" для возврата результирующей строки, отображающей все значащие цифры каждого значения типа Double, показывает, что второе значение .0000000000001 меньше первого.

using System;

public class Example
{
   public static void Main()
   {
      double value1 = 100.10142;
      value1 = Math.Sqrt(Math.Pow(value1, 2));
      double value2 = Math.Pow(value1 * 3.51, 2);
      value2 = Math.Sqrt(value2) / 3.51;
      Console.WriteLine("{0} = {1}: {2}\n",
                        value1, value2, value1.Equals(value2));
      Console.WriteLine("{0:R} = {1:R}", value1, value2);
   }
}
// The example displays the following output:
//    100.10142 = 100.10142: False
//
//    100.10142 = 100.10141999999999
Module Example
   Public Sub Main()
      Dim value1 As Double = 100.10142
      value1 = Math.Sqrt(Math.Pow(value1, 2))
      Dim value2 As Double = Math.Pow(value1 * 3.51, 2)
      value2 = Math.Sqrt(value2) / 3.51
      Console.WriteLine("{0} = {1}: {2}", 
                        value1, value2, value1.Equals(value2)) 
      Console.WriteLine()
      Console.WriteLine("{0:R} = {1:R}", value1, value2) 
   End Sub
End Module
' The example displays the following output:
'    100.10142 = 100.10142: False
'    
'    100.10142 = 100.10141999999999

В случаях, когда вероятность потери точности может повлиять на результат сравнения, можно принять любые из следующих альтернатив для вызова Equals CompareTo метода или:

  • Вызовите Math.Round метод, чтобы убедиться, что оба значения имеют одинаковую точность. Следующий пример изменяет предыдущий пример, чтобы использовать этот подход, чтобы два дробных значения были эквивалентными.

    using System;
    
    public class Example
    {
       public static void Main()
       {
          double value1 = .333333333333333;
          double value2 = 1.0/3;
          int precision = 7;
          value1 = Math.Round(value1, precision);
          value2 = Math.Round(value2, precision);
          Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2));
       }
    }
    // The example displays the following output:
    //        0.3333333 = 0.3333333: True
    
    Module Example
       Public Sub Main()
          Dim value1 As Double = .333333333333333
          Dim value2 As Double = 1/3
          Dim precision As Integer = 7
          value1 = Math.Round(value1, precision)
          value2 = Math.Round(value2, precision)
          Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2))
       End Sub
    End Module
    ' The example displays the following output:
    '       0.3333333 = 0.3333333: True
    

    Проблема точности по-прежнему применима к округлению средних значений. Дополнительные сведения см. в описании метода Math.Round(Double, Int32, MidpointRounding).

  • Проверка на приблизительный равенство, а не на равенство. Для этого необходимо определить абсолютную величину, с которой два значения могут различаться, но по-прежнему равны, или определить относительный объем, на который меньшее значение может отличаться от большего.

    Предупреждение

    Double.Epsilon иногда используется в качестве абсолютной меры расстояния между двумя Double значениями при проверке на равенство. Однако Double.Epsilon измеряет наименьшее возможное значение, которое можно добавить или вычесть из, Double значение которого равно нулю. Для большинства положительных и отрицательных Double значений значение Double.Epsilon слишком мало для обнаружения. Таким образом, за исключением нулевых значений, не рекомендуется использовать его в тестах на равенство.

    В следующем примере используется второй подход для определения IsApproximatelyEqual метода, который проверяет относительное различие между двумя значениями. Он также отличается от результата вызовов IsApproximatelyEqual метода и Equals(Double) метода.

    using System;
    
    public class Example
    {
       public static void Main()
       {
          double one1 = .1 * 10;
          double one2 = 0;
          for (int ctr = 1; ctr <= 10; ctr++)
             one2 += .1;
    
          Console.WriteLine("{0:R} = {1:R}: {2}", one1, one2, one1.Equals(one2));
          Console.WriteLine("{0:R} is approximately equal to {1:R}: {2}",
                            one1, one2,
                            IsApproximatelyEqual(one1, one2, .000000001));
       }
    
       static bool IsApproximatelyEqual(double value1, double value2, double epsilon)
       {
          // If they are equal anyway, just return True.
          if (value1.Equals(value2))
             return true;
    
          // Handle NaN, Infinity.
          if (Double.IsInfinity(value1) | Double.IsNaN(value1))
             return value1.Equals(value2);
          else if (Double.IsInfinity(value2) | Double.IsNaN(value2))
             return value1.Equals(value2);
    
          // Handle zero to avoid division by zero
          double divisor = Math.Max(value1, value2);
          if (divisor.Equals(0))
             divisor = Math.Min(value1, value2);
    
          return Math.Abs((value1 - value2) / divisor) <= epsilon;
       }
    }
    // The example displays the following output:
    //       1 = 0.99999999999999989: False
    //       1 is approximately equal to 0.99999999999999989: True
    
    Module Example
       Public Sub Main()
          Dim one1 As Double = .1 * 10
          Dim one2 As Double = 0
          For ctr As Integer = 1 To 10
             one2 += .1
          Next
          Console.WriteLine("{0:R} = {1:R}: {2}", one1, one2, one1.Equals(one2))
          Console.WriteLine("{0:R} is approximately equal to {1:R}: {2}", 
                            one1, one2, 
                            IsApproximatelyEqual(one1, one2, .000000001))   
       End Sub
    
       Function IsApproximatelyEqual(value1 As Double, value2 As Double, 
                                     epsilon As Double) As Boolean
          ' If they are equal anyway, just return True.
          If value1.Equals(value2) Then Return True
          
          ' Handle NaN, Infinity.
          If Double.IsInfinity(value1) Or Double.IsNaN(value1) Then
             Return value1.Equals(value2)
          Else If Double.IsInfinity(value2) Or Double.IsNaN(value2)
             Return value1.Equals(value2)
          End If
          
          ' Handle zero to avoid division by zero
          Dim divisor As Double = Math.Max(value1, value2)
          If divisor.Equals(0) Then
             divisor = Math.Min(value1, value2)
          End If 
          
          Return Math.Abs((value1 - value2) / divisor) <= epsilon           
       End Function
    End Module
    ' The example displays the following output:
    '       1 = 0.99999999999999989: False
    '       1 is approximately equal to 0.99999999999999989: True
    

Floating-Point значения и исключения

В отличие от операций с целочисленными типами, которые создают исключения в случае переполнения или недопустимых операций, таких как деление на ноль, операции с значениями с плавающей запятой не создают исключения. Вместо этого в исключительных ситуациях результат операции с плавающей запятой равен нулю, плюс бесконечность, отрицательная бесконечность или не является числом (NaN):

  • Если результат операции с плавающей запятой слишком мал для конечного формата, результат равен нулю. Это может произойти при умножении двух очень маленьких чисел, как показано в следующем примере.

    using System;
    
    public class Example
    {
       public static void Main()
       {
          Double value1 = 1.1632875981534209e-225;
          Double value2 = 9.1642346778e-175;
          Double result = value1 * value2;
          Console.WriteLine("{0} * {1} = {2}", value1, value2, result);
          Console.WriteLine("{0} = 0: {1}", result, result.Equals(0.0));
       }
    }
    // The example displays the following output:
    //       1.16328759815342E-225 * 9.1642346778E-175 = 0
    //       0 = 0: True
    
    Module Example
       Public Sub Main()
          Dim value1 As Double = 1.1632875981534209e-225
          Dim value2 As Double = 9.1642346778e-175
          Dim result As Double = value1 * value2
          Console.WriteLine("{0} * {1} = {2}", value1, value2, result)
          Console.WriteLine("{0} = 0: {1}", result, result.Equals(0.0))
       End Sub
    End Module
    ' The example displays the following output:
    '       1.16328759815342E-225 * 9.1642346778E-175 = 0
    '       0 = 0: True
    
  • Если величина результата операции с плавающей запятой превышает диапазон формата назначения, результатом операции будет PositiveInfinity или NegativeInfinity , в зависимости от знака результата. Результатом операции, которая переполняется Double.MaxValue PositiveInfinity , является, а результатом операции, которая переполняется, Double.MinValue является NegativeInfinity , как показано в следующем примере.

    using System;
    
    public class Example
    {
       public static void Main()
       {
          Double value1 = 4.565e153;
          Double value2 = 6.9375e172;
          Double result = value1 * value2;
          Console.WriteLine("PositiveInfinity: {0}",
                             Double.IsPositiveInfinity(result));
          Console.WriteLine("NegativeInfinity: {0}\n",
                            Double.IsNegativeInfinity(result));
    
          value1 = -value1;
          result = value1 * value2;
          Console.WriteLine("PositiveInfinity: {0}",
                             Double.IsPositiveInfinity(result));
          Console.WriteLine("NegativeInfinity: {0}",
                            Double.IsNegativeInfinity(result));
       }
    }
    
    // The example displays the following output:
    //       PositiveInfinity: True
    //       NegativeInfinity: False
    //
    //       PositiveInfinity: False
    //       NegativeInfinity: True
    
    Module Example
       Public Sub Main()
          Dim value1 As Double = 4.565e153
          Dim value2 As Double = 6.9375e172
          Dim result As Double = value1 * value2
          Console.WriteLine("PositiveInfinity: {0}", 
                             Double.IsPositiveInfinity(result))
          Console.WriteLine("NegativeInfinity: {0}", 
                            Double.IsNegativeInfinity(result))
          Console.WriteLine()                  
          value1 = -value1
          result = value1 * value2
          Console.WriteLine("PositiveInfinity: {0}", 
                             Double.IsPositiveInfinity(result))
          Console.WriteLine("NegativeInfinity: {0}", 
                            Double.IsNegativeInfinity(result))
       End Sub
    End Module
    ' The example displays the following output:
    '       PositiveInfinity: True
    '       NegativeInfinity: False
    '       
    '       PositiveInfinity: False
    '       NegativeInfinity: True
    

    PositiveInfinity также результаты из деления на ноль с положительным делимым и NegativeInfinity результатом деления на ноль с отрицательным делимым.

  • Если операция с плавающей запятой является недопустимой, результатом операции будет NaN . Например, NaN результаты выполнения следующих операций:

    • Деление на ноль с делимым на ноль. Обратите внимание, что другие варианты деления на ноль приводят к одному PositiveInfinity или NegativeInfinity .
  • Любая операция с плавающей запятой с недопустимым входом. Например, вызов Math.Sqrt метода с отрицательным значением возвращает NaN , как и вызов Math.Acos метода со значением, которое больше одного или меньше нуля.

  • Любая операция с аргументом, значение которого равно Double.NaN .

Преобразования типов и двойная структура

В Double структуре не определены явные или неявные операторы преобразования. вместо этого преобразования реализуются компилятором.

Преобразование значения любого примитивного числового типа в Double является расширяющим преобразованием и, следовательно, не требует явного приведения или вызова метода преобразования, если только компилятор явно не требует его. например, компилятор C# требует оператора приведения для преобразования из Decimal в Double , а Visual Basic компилятора — нет. В следующем примере минимальное или максимальное значение других примитивных числовых типов преобразуется в Double .

using System;

public class Example
{
   public static void Main()
   {
      dynamic[] values = { Byte.MinValue, Byte.MaxValue, Decimal.MinValue,
                           Decimal.MaxValue, Int16.MinValue, Int16.MaxValue,
                           Int32.MinValue, Int32.MaxValue, Int64.MinValue,
                           Int64.MaxValue, SByte.MinValue, SByte.MaxValue,
                           Single.MinValue, Single.MaxValue, UInt16.MinValue,
                           UInt16.MaxValue, UInt32.MinValue, UInt32.MaxValue,
                           UInt64.MinValue, UInt64.MaxValue };
      double dblValue;
      foreach (var value in values) {
         if (value.GetType() == typeof(Decimal))
            dblValue = (Double) value;
         else
            dblValue = value;
         Console.WriteLine("{0} ({1}) --> {2:R} ({3})",
                           value, value.GetType().Name,
                           dblValue, dblValue.GetType().Name);
      }
   }
}
// The example displays the following output:
//    0 (Byte) --> 0 (Double)
//    255 (Byte) --> 255 (Double)
//    -79228162514264337593543950335 (Decimal) --> -7.9228162514264338E+28 (Double)
//    79228162514264337593543950335 (Decimal) --> 7.9228162514264338E+28 (Double)
//    -32768 (Int16) --> -32768 (Double)
//    32767 (Int16) --> 32767 (Double)
//    -2147483648 (Int32) --> -2147483648 (Double)
//    2147483647 (Int32) --> 2147483647 (Double)
//    -9223372036854775808 (Int64) --> -9.2233720368547758E+18 (Double)
//    9223372036854775807 (Int64) --> 9.2233720368547758E+18 (Double)
//    -128 (SByte) --> -128 (Double)
//    127 (SByte) --> 127 (Double)
//    -3.402823E+38 (Single) --> -3.4028234663852886E+38 (Double)
//    3.402823E+38 (Single) --> 3.4028234663852886E+38 (Double)
//    0 (UInt16) --> 0 (Double)
//    65535 (UInt16) --> 65535 (Double)
//    0 (UInt32) --> 0 (Double)
//    4294967295 (UInt32) --> 4294967295 (Double)
//    0 (UInt64) --> 0 (Double)
//    18446744073709551615 (UInt64) --> 1.8446744073709552E+19 (Double)
Module Example
   Public Sub Main()
      Dim values() As Object = { Byte.MinValue, Byte.MaxValue, Decimal.MinValue,
                                 Decimal.MaxValue, Int16.MinValue, Int16.MaxValue,
                                 Int32.MinValue, Int32.MaxValue, Int64.MinValue,
                                 Int64.MaxValue, SByte.MinValue, SByte.MaxValue,
                                 Single.MinValue, Single.MaxValue, UInt16.MinValue,
                                 UInt16.MaxValue, UInt32.MinValue, UInt32.MaxValue,
                                 UInt64.MinValue, UInt64.MaxValue }
      Dim dblValue As Double
      For Each value In values
         dblValue = value
         Console.WriteLine("{0} ({1}) --> {2:R} ({3})",
                           value, value.GetType().Name,
                           dblValue, dblValue.GetType().Name)
      Next
   End Sub
End Module
' The example displays the following output:
'    0 (Byte) --> 0 (Double)
'    255 (Byte) --> 255 (Double)
'    -79228162514264337593543950335 (Decimal) --> -7.9228162514264338E+28 (Double)
'    79228162514264337593543950335 (Decimal) --> 7.9228162514264338E+28 (Double)
'    -32768 (Int16) --> -32768 (Double)
'    32767 (Int16) --> 32767 (Double)
'    -2147483648 (Int32) --> -2147483648 (Double)
'    2147483647 (Int32) --> 2147483647 (Double)
'    -9223372036854775808 (Int64) --> -9.2233720368547758E+18 (Double)
'    9223372036854775807 (Int64) --> 9.2233720368547758E+18 (Double)
'    -128 (SByte) --> -128 (Double)
'    127 (SByte) --> 127 (Double)
'    -3.402823E+38 (Single) --> -3.4028234663852886E+38 (Double)
'    3.402823E+38 (Single) --> 3.4028234663852886E+38 (Double)
'    0 (UInt16) --> 0 (Double)
'    65535 (UInt16) --> 65535 (Double)
'    0 (UInt32) --> 0 (Double)
'    4294967295 (UInt32) --> 4294967295 (Double)
'    0 (UInt64) --> 0 (Double)
'    18446744073709551615 (UInt64) --> 1.8446744073709552E+19 (Double)

Кроме того, Single значения, Single.NaN Single.PositiveInfinity и Single.NegativeInfinity преобразуются в Double.NaN , Double.PositiveInfinity и Double.NegativeInfinity соответственно.

Обратите внимание, что преобразование значения некоторых числовых типов в Double значение может привести к утрате точности. Как показано в примере, возможна утрата точности при преобразовании Decimal значений, Int64 и UInt64 в Double значения.

преобразование Double значения в значение любого другого числового типа данных-примитива является узким преобразованием и требует оператора приведения (в C#), метода преобразования (в Visual Basic) или вызова Convert метода. Значения, находящиеся за пределами диапазона целевого типа данных, определяемые MinValue свойствами и свойств целевого типа MaxValue , ведут себя так, как показано в следующей таблице.

Тип результирующего значения Результат
Любой целочисленный тип OverflowExceptionИсключение, если преобразование происходит в проверяемом контексте.

Если преобразование происходит в непроверяемом контексте (по умолчанию в C#), операция преобразования выполняется успешно, но значение переполняется.
Decimal Исключение OverflowException.
Single Single.NegativeInfinity для отрицательных значений.

Single.PositiveInfinity для положительных значений.

Кроме того, Double.NaN , Double.PositiveInfinity и Double.NegativeInfinity вызывают OverflowException для преобразования в целые числа в проверяемом контексте, но эти значения переполняются при преобразовании в целые числа в непроверяемом контексте. Для преобразований Decimal они всегда создают исключение OverflowException . Для преобразований Single они преобразуют в Single.NaN , Single.PositiveInfinity и Single.NegativeInfinity соответственно.

Обратите внимание, что при преобразовании Double значения в другой числовой тип может произойти утрата точности. в случае преобразования в любой из целочисленных типов, как показано в выходных данных примера, дробный компонент теряется, когда Double значение округляется (как в Visual Basic) или усекается (как в C#). Для преобразований Decimal в Single значения и Double значение может не иметь точного представления в целевом типе данных.

В следующем примере число значений преобразуется Double в несколько других числовых типов. преобразования выполняются в проверяемом контексте в Visual Basic (по умолчанию) и в C# (из-за ключевого слова checked ). Выходные данные в примере показывают результат для преобразований в проверяемом непроверяемом контексте. вы можете выполнять преобразования в непроверенном контексте в Visual Basic путем компиляции с помощью /removeintchecks+ параметра компилятора и в C#, закомментируя checked инструкцию.

using System;

public class Example
{
   public static void Main()
   {
      Double[] values = { Double.MinValue, -67890.1234, -12345.6789,
                          12345.6789, 67890.1234, Double.MaxValue,
                          Double.NaN, Double.PositiveInfinity,
                          Double.NegativeInfinity };
      checked {
         foreach (var value in values) {
            try {
                Int64 lValue = (long) value;
                Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
                                  value, value.GetType().Name,
                                  lValue, lValue.GetType().Name);
            }
            catch (OverflowException) {
               Console.WriteLine("Unable to convert {0} to Int64.", value);
            }
            try {
                UInt64 ulValue = (ulong) value;
                Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
                                  value, value.GetType().Name,
                                  ulValue, ulValue.GetType().Name);
            }
            catch (OverflowException) {
               Console.WriteLine("Unable to convert {0} to UInt64.", value);
            }
            try {
                Decimal dValue = (decimal) value;
                Console.WriteLine("{0} ({1}) --> {2} ({3})",
                                  value, value.GetType().Name,
                                  dValue, dValue.GetType().Name);
            }
            catch (OverflowException) {
               Console.WriteLine("Unable to convert {0} to Decimal.", value);
            }
            try {
                Single sValue = (float) value;
                Console.WriteLine("{0} ({1}) --> {2} ({3})",
                                  value, value.GetType().Name,
                                  sValue, sValue.GetType().Name);
            }
            catch (OverflowException) {
               Console.WriteLine("Unable to convert {0} to Single.", value);
            }
            Console.WriteLine();
         }
      }
   }
}
// The example displays the following output for conversions performed
// in a checked context:
//       Unable to convert -1.79769313486232E+308 to Int64.
//       Unable to convert -1.79769313486232E+308 to UInt64.
//       Unable to convert -1.79769313486232E+308 to Decimal.
//       -1.79769313486232E+308 (Double) --> -Infinity (Single)
//
//       -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
//       Unable to convert -67890.1234 to UInt64.
//       -67890.1234 (Double) --> -67890.1234 (Decimal)
//       -67890.1234 (Double) --> -67890.13 (Single)
//
//       -12345.6789 (Double) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
//       Unable to convert -12345.6789 to UInt64.
//       -12345.6789 (Double) --> -12345.6789 (Decimal)
//       -12345.6789 (Double) --> -12345.68 (Single)
//
//       12345.6789 (Double) --> 12345 (0x0000000000003039) (Int64)
//       12345.6789 (Double) --> 12345 (0x0000000000003039) (UInt64)
//       12345.6789 (Double) --> 12345.6789 (Decimal)
//       12345.6789 (Double) --> 12345.68 (Single)
//
//       67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
//       67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
//       67890.1234 (Double) --> 67890.1234 (Decimal)
//       67890.1234 (Double) --> 67890.13 (Single)
//
//       Unable to convert 1.79769313486232E+308 to Int64.
//       Unable to convert 1.79769313486232E+308 to UInt64.
//       Unable to convert 1.79769313486232E+308 to Decimal.
//       1.79769313486232E+308 (Double) --> Infinity (Single)
//
//       Unable to convert NaN to Int64.
//       Unable to convert NaN to UInt64.
//       Unable to convert NaN to Decimal.
//       NaN (Double) --> NaN (Single)
//
//       Unable to convert Infinity to Int64.
//       Unable to convert Infinity to UInt64.
//       Unable to convert Infinity to Decimal.
//       Infinity (Double) --> Infinity (Single)
//
//       Unable to convert -Infinity to Int64.
//       Unable to convert -Infinity to UInt64.
//       Unable to convert -Infinity to Decimal.
//       -Infinity (Double) --> -Infinity (Single)
// The example displays the following output for conversions performed
// in an unchecked context:
//       -1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       -1.79769313486232E+308 (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
//       Unable to convert -1.79769313486232E+308 to Decimal.
//       -1.79769313486232E+308 (Double) --> -Infinity (Single)
//
//       -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
//       -67890.1234 (Double) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
//       -67890.1234 (Double) --> -67890.1234 (Decimal)
//       -67890.1234 (Double) --> -67890.13 (Single)
//
//       -12345.6789 (Double) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
//       -12345.6789 (Double) --> 18446744073709539271 (0xFFFFFFFFFFFFCFC7) (UInt64)
//       -12345.6789 (Double) --> -12345.6789 (Decimal)
//       -12345.6789 (Double) --> -12345.68 (Single)
//
//       12345.6789 (Double) --> 12345 (0x0000000000003039) (Int64)
//       12345.6789 (Double) --> 12345 (0x0000000000003039) (UInt64)
//       12345.6789 (Double) --> 12345.6789 (Decimal)
//       12345.6789 (Double) --> 12345.68 (Single)
//
//       67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
//       67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
//       67890.1234 (Double) --> 67890.1234 (Decimal)
//       67890.1234 (Double) --> 67890.13 (Single)
//
//       1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       1.79769313486232E+308 (Double) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert 1.79769313486232E+308 to Decimal.
//       1.79769313486232E+308 (Double) --> Infinity (Single)
//
//       NaN (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       NaN (Double) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert NaN to Decimal.
//       NaN (Double) --> NaN (Single)
//
//       Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       Infinity (Double) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert Infinity to Decimal.
//       Infinity (Double) --> Infinity (Single)
//
//       -Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       -Infinity (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
//       Unable to convert -Infinity to Decimal.
//       -Infinity (Double) --> -Infinity (Single)
Module Example
   Public Sub Main()
      Dim values() As Double = { Double.MinValue, -67890.1234, -12345.6789,
                                 12345.6789, 67890.1234, Double.MaxValue,
                                 Double.NaN, Double.PositiveInfinity,
                                 Double.NegativeInfinity }
      For Each value In values
         Try
             Dim lValue As Int64 = CLng(value)
             Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
                               value, value.GetType().Name,
                               lValue, lValue.GetType().Name)
         Catch e As OverflowException
            Console.WriteLine("Unable to convert {0} to Int64.", value)
         End Try
         Try
             Dim ulValue As UInt64 = CULng(value)
             Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
                               value, value.GetType().Name,
                               ulValue, ulValue.GetType().Name)
         Catch e As OverflowException
            Console.WriteLine("Unable to convert {0} to UInt64.", value)
         End Try
         Try
             Dim dValue As Decimal = CDec(value)
             Console.WriteLine("{0} ({1}) --> {2} ({3})",
                               value, value.GetType().Name,
                               dValue, dValue.GetType().Name)
         Catch e As OverflowException
            Console.WriteLine("Unable to convert {0} to Decimal.", value)
         End Try
         Try
             Dim sValue As Single = CSng(value)
             Console.WriteLine("{0} ({1}) --> {2} ({3})",
                               value, value.GetType().Name,
                               sValue, sValue.GetType().Name)
         Catch e As OverflowException
            Console.WriteLine("Unable to convert {0} to Single.", value)
         End Try
         Console.WriteLine()
      Next
   End Sub
End Module
' The example displays the following output for conversions performed
' in a checked context:
'       Unable to convert -1.79769313486232E+308 to Int64.
'       Unable to convert -1.79769313486232E+308 to UInt64.
'       Unable to convert -1.79769313486232E+308 to Decimal.
'       -1.79769313486232E+308 (Double) --> -Infinity (Single)
'
'       -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
'       Unable to convert -67890.1234 to UInt64.
'       -67890.1234 (Double) --> -67890.1234 (Decimal)
'       -67890.1234 (Double) --> -67890.13 (Single)
'
'       -12345.6789 (Double) --> -12346 (0xFFFFFFFFFFFFCFC6) (Int64)
'       Unable to convert -12345.6789 to UInt64.
'       -12345.6789 (Double) --> -12345.6789 (Decimal)
'       -12345.6789 (Double) --> -12345.68 (Single)
'
'       12345.6789 (Double) --> 12346 (0x000000000000303A) (Int64)
'       12345.6789 (Double) --> 12346 (0x000000000000303A) (UInt64)
'       12345.6789 (Double) --> 12345.6789 (Decimal)
'       12345.6789 (Double) --> 12345.68 (Single)
'
'       67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
'       67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
'       67890.1234 (Double) --> 67890.1234 (Decimal)
'       67890.1234 (Double) --> 67890.13 (Single)
'
'       Unable to convert 1.79769313486232E+308 to Int64.
'       Unable to convert 1.79769313486232E+308 to UInt64.
'       Unable to convert 1.79769313486232E+308 to Decimal.
'       1.79769313486232E+308 (Double) --> Infinity (Single)
'
'       Unable to convert NaN to Int64.
'       Unable to convert NaN to UInt64.
'       Unable to convert NaN to Decimal.
'       NaN (Double) --> NaN (Single)
'
'       Unable to convert Infinity to Int64.
'       Unable to convert Infinity to UInt64.
'       Unable to convert Infinity to Decimal.
'       Infinity (Double) --> Infinity (Single)
'
'       Unable to convert -Infinity to Int64.
'       Unable to convert -Infinity to UInt64.
'       Unable to convert -Infinity to Decimal.
'       -Infinity (Double) --> -Infinity (Single)
' The example displays the following output for conversions performed
' in an unchecked context:
'       -1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       -1.79769313486232E+308 (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
'       Unable to convert -1.79769313486232E+308 to Decimal.
'       -1.79769313486232E+308 (Double) --> -Infinity (Single)
'
'       -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
'       -67890.1234 (Double) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
'       -67890.1234 (Double) --> -67890.1234 (Decimal)
'       -67890.1234 (Double) --> -67890.13 (Single)
'
'       -12345.6789 (Double) --> -12346 (0xFFFFFFFFFFFFCFC6) (Int64)
'       -12345.6789 (Double) --> 18446744073709539270 (0xFFFFFFFFFFFFCFC6) (UInt64)
'       -12345.6789 (Double) --> -12345.6789 (Decimal)
'       -12345.6789 (Double) --> -12345.68 (Single)
'
'       12345.6789 (Double) --> 12346 (0x000000000000303A) (Int64)
'       12345.6789 (Double) --> 12346 (0x000000000000303A) (UInt64)
'       12345.6789 (Double) --> 12345.6789 (Decimal)
'       12345.6789 (Double) --> 12345.68 (Single)
'
'       67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
'       67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
'       67890.1234 (Double) --> 67890.1234 (Decimal)
'       67890.1234 (Double) --> 67890.13 (Single)
'
'       1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       1.79769313486232E+308 (Double) --> 0 (0x0000000000000000) (UInt64)
'       Unable to convert 1.79769313486232E+308 to Decimal.
'       1.79769313486232E+308 (Double) --> Infinity (Single)
'
'       NaN (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       NaN (Double) --> 0 (0x0000000000000000) (UInt64)
'       Unable to convert NaN to Decimal.
'       NaN (Double) --> NaN (Single)
'
'       Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       Infinity (Double) --> 0 (0x0000000000000000) (UInt64)
'       Unable to convert Infinity to Decimal.
'       Infinity (Double) --> Infinity (Single)
'
'       -Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       -Infinity (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
'       Unable to convert -Infinity to Decimal.
'       -Infinity (Double) --> -Infinity (Single)

дополнительные сведения о преобразовании числовых типов см. в разделе преобразование типов в таблицах платформа .NET Framework и преобразования типов.

Floating-Point функциональности

DoubleСтруктура и связанные типы предоставляют методы для выполнения операций в следующих областях:

  • Сравнение значений. Можно вызвать метод, Equals чтобы определить Double , равны ли два значения, или CompareTo метод для определения связи между двумя значениями.

    DoubleСтруктура также поддерживает полный набор операторов сравнения. Например, можно проверить на равенство или неравенство или определить, является ли одно значение больше или равно другому. Если один из операндов является числовым типом, отличным от Double , то Double перед выполнением сравнения он преобразуется в.

    Предупреждение

    Из-за различий в точности два Double значения, которые должны быть равны, могут быть неравными, что влияет на результат сравнения. Дополнительные сведения о сравнении двух значений см. в разделе Проверка на равенство Double .

    IsNaN IsInfinity IsPositiveInfinity IsNegativeInfinity Для проверки этих специальных значений можно также вызвать методы,, и.

  • Математические операции. Общие арифметические операции, такие как сложение, вычитание, умножение и деление, реализуются компиляторами языка и инструкциями на языке CIL, а не Double методами. Если один из операндов в математической операции является числовым типом, отличным от Double , то Double перед выполнением операции он преобразуется в. Результат операции также является Double значением.

    другие математические операции можно выполнить, вызвав static Shared методы (в Visual Basic) в System.Math классе. Он содержит дополнительные методы, обычно используемые для арифметических операций (например Math.Abs ,, Math.Sign и Math.Sqrt ), Geometry (например, Math.Cos и Math.Sin ) и математического анализа за (например, Math.Log ).

    Можно также манипулировать отдельными битами Double значения. BitConverter.DoubleToInt64BitsМетод сохраняет Double битовый шаблон значения в 64-разрядном целом число. BitConverter.GetBytes(Double)Метод возвращает свой битовый шаблон в массиве байтов.

  • Округление. Округление часто используется как метод снижения влияния различий между значениями, вызванными проблемами представления и точности с плавающей запятой. Можно округлить Double значение, вызвав Math.Round метод.

  • Форматирование. Значение можно преобразовать Double в строковое представление, вызвав ToString метод или воспользовавшись функцией составного форматирования. Сведения о том, как строки формата управляют строковым представлением значений с плавающей запятой, см. в разделах стандартные строки числовых форматов и строки настраиваемых числовых форматов .

  • Синтаксический анализ строк. Можно преобразовать строковое представление значения с плавающей запятой в Double значение, вызвав либо метод, либо Parse TryParse . Если операция синтаксического анализа завершается неудачно, Parse метод создает исключение, в то время как TryParse метод возвращает false .

  • Преобразование типов. Doubleструктура предоставляет явную реализацию интерфейса для IConvertible интерфейса, который поддерживает преобразование между любыми двумя стандартными платформа .NET Frameworkными типами данных. Языковые компиляторы также поддерживают неявное преобразование значений всех других стандартных числовых типов в Double значения. Преобразование значения любого стандартного числового типа в Double является расширяющим преобразованием и не требует использования пользователем оператора приведения или метода преобразования.

    Однако преобразование Int64 Single значений и может привести к утрате точности. В следующей таблице перечислены различия в точности для каждого из этих типов:

    Type Максимальная точность Внутренняя точность
    Double 15 17
    Int64 19 десятичных знаков 19 десятичных знаков
    Single 7 десятичных знаков 9 десятичных знаков

    Проблема точности чаще всего влияет на Single значения, которые преобразуются в Double значения. В следующем примере два значения, созданные идентичными операциями деления, не равны, так как одно из значений является значением с плавающей запятой одиночной точности, преобразованным в Double .

    using System;
    
    public class Example
    {
       public static void Main()
       {
          Double value = .1;
          Double result1 = value * 10;
          Double result2 = 0;
          for (int ctr = 1; ctr <= 10; ctr++)
             result2 += value;
    
          Console.WriteLine(".1 * 10:           {0:R}", result1);
          Console.WriteLine(".1 Added 10 times: {0:R}", result2);
       }
    }
    // The example displays the following output:
    //       .1 * 10:           1
    //       .1 Added 10 times: 0.99999999999999989
    
    Module Example
       Public Sub Main()
          Dim value As Double = .1
          Dim result1 As Double = value * 10
          Dim result2 As Double
          For ctr As Integer = 1 To 10
             result2 += value
          Next
          Console.WriteLine(".1 * 10:           {0:R}", result1)
          Console.WriteLine(".1 Added 10 times: {0:R}", result2)
       End Sub
    End Module
    ' The example displays the following output:
    '       .1 * 10:           1
    '       .1 Added 10 times: 0.99999999999999989
    

Поля

Epsilon

Представляет наименьшее положительное значение Double больше нуля. Это поле является константой.

MaxValue

Представляет наибольшее возможное значение типа Double. Это поле является константой.

MinValue

Представляет минимально допустимое значение типа Double. Это поле является константой.

NaN

Представляет значение, не являющееся числом (NaN). Это поле является константой.

NegativeInfinity

Представляет минус бесконечность. Это поле является константой.

PositiveInfinity

Представляет плюс бесконечность. Это поле является константой.

Методы

CompareTo(Double)

Сравнивает данный экземпляр с заданным числом двойной точности с плавающей запятой и возвращает целое число, которое показывает, является ли значение данного экземпляра меньше, больше или равно значению заданного числа двойной точности с плавающей запятой.

CompareTo(Object)

Сравнивает данный экземпляр с указанным объектом и возвращает целое число, которое показывает, является ли значение данного экземпляра меньше, больше или равно значению заданного объекта.

Equals(Double)

Возвращает значение, позволяющее определить, представляют ли этот экземпляр и заданный объект Double одно и то же значение.

Equals(Object)

Возвращает значение, показывающее, равен ли данный экземпляр заданному объекту.

GetHashCode()

Возвращает хэш-код данного экземпляра.

GetTypeCode()

Возвращает TypeCode для типа значения Double.

IsFinite(Double)

Определяет, является ли указанное значение конечным (нулевым, поднормальным или нормальным).

IsInfinity(Double)

Возвращает значение, позволяющее определить, равно ли данное число плюс или минус бесконечности.

IsNaN(Double)

Возвращает значение, показывающее, что указанное значение не является числом (NaN).

IsNegative(Double)

Определяет, является ли заданное значение отрицательным.

IsNegativeInfinity(Double)

Возвращает значение, позволяющее определить, равно ли данное число минус бесконечности.

IsNormal(Double)

Определяет, является ли заданное значение нормальным.

IsPositiveInfinity(Double)

Возвращает значение, показывающее, равно ли данное число плюс бесконечности.

IsSubnormal(Double)

Определяет, является ли заданное значение поднормальным.

Parse(ReadOnlySpan<Char>, NumberStyles, IFormatProvider)

Преобразует диапазон символов, содержащий строковое представление числа в указанном стиле и с использованием формата, соответствующего данному языку и региональным параметрам, в эквивалентное ему число с плавающей запятой двойной точности.

Parse(String)

Преобразует строковое представление числа в эквивалентное ему число двойной точности с плавающей запятой.

Parse(String, IFormatProvider)

Преобразует строковое представление числа, выраженное в заданном формате, связанном с языком и региональными параметрами, в эквивалентное ему число двойной точности с плавающей запятой.

Parse(String, NumberStyles)

Преобразует строковое представление числа указанного стиля в эквивалентное ему число двойной точности с плавающей запятой.

Parse(String, NumberStyles, IFormatProvider)

Преобразует строковое представление числа указанного стиля, выраженное в формате, соответствующем определенному языку и региональным параметрам, в эквивалентное ему число двойной точности с плавающей запятой.

ToString()

Преобразует числовое значение данного экземпляра в эквивалентное ему строковое представление.

ToString(IFormatProvider)

Преобразует числовое значение данного экземпляра в эквивалентное ему строковое представление с использованием указанных сведений об особенностях форматирования для данного языка и региональных параметров.

ToString(String)

Преобразует числовое значение данного экземпляра в эквивалентное строковое представление с использованием указанного формата.

ToString(String, IFormatProvider)

Преобразует числовое значение данного экземпляра в эквивалентное ему строковое представление с использованием указанного формата и сведений об особенностях форматирования для данного языка и региональных параметров.

TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider)

Пытается отформатировать значение текущего двойного экземпляра в указанный диапазон символов.

TryParse(ReadOnlySpan<Char>, Double)

Преобразует представление диапазона числа указанного стиля, выраженное в формате, соответствующем определенному языку и региональным параметрам, в эквивалентное ему число двойной точности с плавающей запятой. Возвращает значение, указывающее, успешно ли выполнено преобразование.

TryParse(ReadOnlySpan<Char>, NumberStyles, IFormatProvider, Double)

Преобразует диапазон символов, содержащий строковое представление числа в указанном стиле и с использованием формата, соответствующего данному языку и региональным параметрам, в эквивалентное ему число с плавающей запятой двойной точности. Возвращает значение, указывающее, успешно ли выполнено преобразование.

TryParse(String, Double)

Преобразует строковое представление числа в эквивалентное ему число двойной точности с плавающей запятой. Возвращает значение, указывающее, успешно ли выполнено преобразование.

TryParse(String, NumberStyles, IFormatProvider, Double)

Преобразует строковое представление числа указанного стиля, выраженное в формате, соответствующем определенному языку и региональным параметрам, в эквивалентное ему число двойной точности с плавающей запятой. Возвращает значение, указывающее, успешно ли выполнено преобразование.

Операторы

Equality(Double, Double)

Возвращает значение, указывающее, равны ли два заданных значения Double.

GreaterThan(Double, Double)

Возвращает значение, указывающее, действительно ли заданное значение Double больше другого заданного значения Double.

GreaterThanOrEqual(Double, Double)

Возвращает значение, указывающее, действительно ли заданное значение Double больше или равно другому заданному значению Double.

Inequality(Double, Double)

Возвращает значение, указывающее, не равны ли два заданных значения Double.

LessThan(Double, Double)

Возвращает значение, указывающее, действительно ли заданное значение Double меньше другого заданного значения Double.

LessThanOrEqual(Double, Double)

Возвращает значение, указывающее, действительно ли заданное значение Double меньше или равно другому заданному значению Double.

Явные реализации интерфейса

IComparable.CompareTo(Object)

Сравнивает текущий экземпляр с другим объектом того же типа и возвращает целое число, которое показывает, расположен ли текущий экземпляр перед, после или на той же позиции в порядке сортировки, что и другой объект.

IConvertible.GetTypeCode()

Возвращает TypeCode для этого экземпляра.

IConvertible.ToBoolean(IFormatProvider)

Описание этого члена см. в разделе ToBoolean(IFormatProvider).

IConvertible.ToByte(IFormatProvider)

Описание этого члена см. в разделе ToByte(IFormatProvider).

IConvertible.ToChar(IFormatProvider)

Это преобразование не поддерживается. При попытке использовать этот метод выбрасывается исключение InvalidCastException.

IConvertible.ToDateTime(IFormatProvider)

Это преобразование не поддерживается. При попытке использовать этот метод выбрасывается исключение InvalidCastException.

IConvertible.ToDecimal(IFormatProvider)

Описание этого члена см. в разделе ToDecimal(IFormatProvider).

IConvertible.ToDouble(IFormatProvider)

Описание этого члена см. в разделе ToDouble(IFormatProvider).

IConvertible.ToInt16(IFormatProvider)

Описание этого члена см. в разделе ToInt16(IFormatProvider).

IConvertible.ToInt32(IFormatProvider)

Описание этого члена см. в разделе ToInt32(IFormatProvider).

IConvertible.ToInt64(IFormatProvider)

Описание этого члена см. в разделе ToInt64(IFormatProvider).

IConvertible.ToSByte(IFormatProvider)

Описание этого члена см. в разделе ToSByte(IFormatProvider).

IConvertible.ToSingle(IFormatProvider)

Описание этого члена см. в разделе ToSingle(IFormatProvider).

IConvertible.ToType(Type, IFormatProvider)

Описание этого члена см. в разделе ToType(Type, IFormatProvider).

IConvertible.ToUInt16(IFormatProvider)

Описание этого члена см. в разделе ToUInt16(IFormatProvider).

IConvertible.ToUInt32(IFormatProvider)

Описание этого члена см. в разделе ToUInt32(IFormatProvider).

IConvertible.ToUInt64(IFormatProvider)

Описание этого члена см. в разделе ToUInt64(IFormatProvider).

Применяется к

Потокобезопасность

Все члены этого типа являются потокобезопасными. Члены, которые могут изменить состояние экземпляра, в действительности возвращают новый экземпляр, инициализированный новым значением. Как с любым другим типом, чтение и запись общей переменной, которая содержит экземпляр этого типа, должны быть защищены блокировкой для обеспечения потокобезопасности.

См. также раздел