次の方法で共有


System.Single.Equals メソッド

この記事では、この API のリファレンス ドキュメントへの補足的な解説を提供します。

このメソッドは Single.Equals(Single) インターフェイスを実装し、パラメーターを System.IEquatable<T> オブジェクトに変換する必要がないため、少し優れた Single.Equals(Object) パフォーマンスを obj 発揮します。

拡大変換

プログラミング言語によっては、パラメーター型のビット数がインスタンス型よりも少ない (狭い) メソッドをコーディング Equals できる場合があります。 これは、一部のプログラミング言語では、インスタンスと同数のビットを持つ型としてパラメーターを表す暗黙的な拡大変換を実行するためです。

たとえば、インスタンスの型が 〗 Single で、パラメーターの型が 〘 であると Int32します。 Microsoft C# コンパイラは、パラメーターの値をオブジェクトとして Single 表す命令を生成し、インスタンスの値とパラメーターの拡大表現を比較するメソッドを生成 Single.Equals(Single) します。

コンパイラが数値型の暗黙的な拡大変換を実行するかどうかを判断するには、プログラミング言語のドキュメントを参照してください。 詳細については、「型変換テーブル」を参照してください

比較の精度

このメソッドは Equals 、2 つの値の精度が異なるため、2 つの明らかに同等の値が等しくない可能性があるため、注意して使用する必要があります。 次の例では、 Single 値 .3333 と Single 1 を 3 で除算して返される値が等しくないと報告します。

// Initialize two floats with apparently identical values
float float1 = .33333f;
float float2 = 1/3;
// Compare them for equality
Console.WriteLine(float1.Equals(float2));    // displays false
// Initialize two floats with apparently identical values
let float1 = 0.33333f
let float2 = 1f / 3f
// Compare them for equality
printfn $"{float1.Equals float2}"    // displays false
' Initialize two singles with apparently identical values
Dim single1 As Single = .33333
Dim single2 As Single = 1/3
' Compare them for equality
Console.WriteLine(single1.Equals(single2))    ' displays False

等価性の比較に関連する問題を回避する 1 つの比較手法では、2 つの値の差の許容できるマージン (値の 1 つの .01% など) を定義する必要があります。 2 つの値の差の絶対値がその余白以下の場合、その差は精度の差の結果である可能性が高く、したがって、値は等しい可能性があります。 次の例では、この手法を使用して .33333 と 1/3 を比較します。これは、前のコード例で等しくないと判明した 2 つの Single 値です。

// Initialize two floats with apparently identical values
float float1 = .33333f;
float float2 = (float) 1/3;
// Define the tolerance for variation in their values
float difference = Math.Abs(float1 * .0001f);

// Compare the values
// The output to the console indicates that the two values are equal
if (Math.Abs(float1 - float2) <= difference)
   Console.WriteLine("float1 and float2 are equal.");
else
   Console.WriteLine("float1 and float2 are unequal.");
// Initialize two floats with apparently identical values
let float1 = 0.33333f
let float2 = 1f / 3f
// Define the tolerance for variation in their values
let difference = abs (float1 * 0.0001f)

// Compare the values
// The output to the console indicates that the two values are equal
if abs (float1 - float2) <= difference then
    printfn "float1 and float2 are equal."
else
    printfn "float1 and float2 are unequal."
' Initialize two singles with apparently identical values
Dim single1 As Single = .33333
Dim single2 As Single = 1/3
' Define the tolerance for variation in their values
Dim difference As Single = Math.Abs(single1 * .0001f)

' Compare the values
' The output to the console indicates that the two values are equal
If Math.Abs(single1 - single2) <= difference Then
   Console.WriteLine("single1 and single2 are equal.")
Else
   Console.WriteLine("single1 and single2 are unequal.")
End If

この場合、値は等しくなります。

Note

範囲が 0 に近い正の値の最小式を定義するため Epsilon 、差の余白は < a0/& より Epsilon大きくする必要があります。 通常、それは何倍も Epsilon大きいです. このため、値の等価性をDouble比較するときは使用Epsilonしないことをお勧めします。

等価性の比較に関連する問題を回避する 2 つ目の手法では、2 つの浮動小数点数とある絶対値の差を比較する必要があります。 差がその絶対値以下の場合、数値は等しくなります。 大きい場合、数値は等しくありません。 これを行う方法の 1 つは、絶対値を任意に選択する方法です。 ただし、許容できる差は値の Single 大きさに依存するため、これは問題になります。 2 つ目の方法では、浮動小数点形式のデザイン機能を利用します。2 つの浮動小数点値の整数表現における仮数成分の違いは、2 つの値を区切る可能な浮動小数点値の数を示します。 たとえば、0.0 と Epsilon 1 の差は、値が 0 である場合Singleに最も小さい表現可能な値であるためEpsilonです。 次の例では、この手法を使用して .33333 と 1/3 を比較します。これは、前のコード例とEquals(Single)メソッドが等しくないと判明した 2 つのDouble値です。 この例では、and BitConverter.ToInt32 メソッドをBitConverter.GetBytes使用して単精度浮動小数点値を整数表現に変換することに注意してください。

using System;

public class Example
{
   public static void Main()
   {
      float value1 = .1f * 10f;
      float value2 = 0f;
      for (int ctr = 0; ctr < 10; ctr++)
         value2 += .1f;
         
      Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
                        HasMinimalDifference(value1, value2, 1));
   }

   public static bool HasMinimalDifference(float value1, float value2, int units)
   {
      byte[] bytes = BitConverter.GetBytes(value1);
      int iValue1 = BitConverter.ToInt32(bytes, 0);
      
      bytes = BitConverter.GetBytes(value2);
      int iValue2 = BitConverter.ToInt32(bytes, 0);
      
      // If the signs are different, return false except for +0 and -0.
      if ((iValue1 >> 31) != (iValue2 >> 31))
      {
         if (value1 == value2)
            return true;
          
         return false;
      }

      int diff = Math.Abs(iValue1 - iValue2);

      if (diff <= units)
         return true;

      return false;
   }
}
// The example displays the following output:
//        1 = 1.00000012: True
open System

let hasMinimalDifference (value1: float32) (value2: float32) units =
    let bytes = BitConverter.GetBytes value1
    let iValue1 = BitConverter.ToInt32(bytes, 0)
    let bytes = BitConverter.GetBytes(value2)
    let iValue2 = BitConverter.ToInt32(bytes, 0)
    
    // If the signs are different, return false except for +0 and -0.
    if (iValue1 >>> 31) <> (iValue2 >>> 31) then
        value1 = value2
    else
        let diff = abs (iValue1 - iValue2)
        diff <= units

let value1 = 0.1f * 10f
let value2 =
    List.replicate 10 0.1f
    |> List.sum
    
printfn $"{value1:R} = {value2:R}: {hasMinimalDifference value1 value2 1}"
// The example displays the following output:
//        1 = 1.0000001: True
Module Example
   Public Sub Main()
      Dim value1 As Single = .1 * 10
      Dim value2 As Single = 0
      For ctr As Integer =  0 To 9
         value2 += CSng(.1)
      Next
               
      Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
                        HasMinimalDifference(value1, value2, 1))
   End Sub

   Public Function HasMinimalDifference(value1 As Single, value2 As Single, units As Integer) As Boolean
      Dim bytes() As Byte = BitConverter.GetBytes(value1)
      Dim iValue1 As Integer =  BitConverter.ToInt32(bytes, 0)
      
      bytes = BitConverter.GetBytes(value2)
      Dim iValue2 As Integer =  BitConverter.ToInt32(bytes, 0)
      
      ' If the signs are different, Return False except for +0 and -0.
      If ((iValue1 >> 31) <> (iValue2 >> 31)) Then
         If value1 = value2 Then
            Return True
         End If           
         Return False
      End If

      Dim diff As Integer =  Math.Abs(iValue1 - iValue2)

      If diff <= units Then
         Return True
      End If

      Return False
   End Function
End Module
' The example displays the following output:
'       1 = 1.00000012: True

文書化された精度を超える浮動小数点数の精度は、.NET の実装とバージョンに固有です。 したがって、2 つの数値を比較すると、.NET のバージョンによって結果が異なる場合があります。これは、数値の内部表現の精度が変わる可能性があるためです。