System.Single.Equals 方法

本文提供了此 API 参考文档的补充说明。

该方法 Single.Equals(Single) 实现 System.IEquatable<T> 接口,并且性能略高于 Single.Equals(Object) 因为它不必将 obj 参数转换为对象。

扩大转换

根据编程语言,可以编写参数 Equals 类型比实例类型更少的位(较窄)的方法进行编码。 这是可能的,因为一些编程语言执行隐式扩大转换,该转换将参数表示为一个类型,其位数与实例数相同。

例如,假设实例类型为 Single ,参数类型为 Int32. Microsoft C# 编译器生成用于将参数值表示为 Single 对象的说明,然后生成一个 Single.Equals(Single) 方法,用于比较实例的值和参数的扩大表示形式。

请参阅编程语言的文档,以确定其编译器是否执行数字类型的隐式扩展转换。 有关详细信息,请参阅 类型转换表

比较中的精度

Equals应谨慎使用该方法,因为两个明显等效的值可能不相等,因为两个值的精度不同。 下面的示例报告 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

避免与比较相等相关的问题的一种比较技术涉及定义两个值之间的可接受的差差(如其中一个值的 .01%)。 如果两个值的绝对值小于或等于该边距,则差值可能是精度差异的结果,因此,这些值可能相等。 以下示例使用此技术比较 .33333 和 1/3,这是上一个代码示例发现不相等的两 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

在这种情况下,这些值相等。

注意

由于 Epsilon 定义其范围接近零的正值的最小表达式,因此差差的边距必须大于 Epsilon。 通常,它比 Epsilon。 因此,建议不要在比较Double相等值时使用Epsilon

避免与比较相等相关的问题的第二种方法涉及比较两个浮点数与某些绝对值之间的差异。 如果差值小于或等于该绝对值,则数字相等。 如果大于,则数字不相等。 执行此操作的一种方法是任意选择绝对值。 但是,这是有问题的,因为可接受的差异边距取决于值的大小 Single 。 第二种方法利用浮点格式的设计特征:两个浮点值的整数表示形式中的 mantissa 组件之间的差异指示分隔两个值的可能浮点值数。 例如,0.0 和 Epsilon 1 之间的差异,因为在 Epsilon 处理 Single 其值为零的数值时是最小的可表示值。 下面的示例使用此技术比较 .33333 和 1/3,这是上一个代码示例Equals(Single)与方法不相等的两Double个值。 请注意,该示例使用 BitConverter.GetBytesBitConverter.ToInt32 方法将单精度浮点值转换为其整数表示形式。

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 的实现和版本。 因此,根据 .NET 的版本,两个数字的比较可能会产生不同的结果,因为数字的内部表示形式的精度可能会改变。