System.Single.Equals 方法

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

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

扩大转换

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

例如,假设实例类型为 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

一种避免比较相等相关问题的比较技术涉及定义两个值之间的可接受差异幅度(如其中一个值的 0.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 大很多倍。 因此,建议不要在比较Epsilon相等值时使用Double

避免与比较相等相关的问题的第二种方法涉及比较两个浮点数与某些绝对值之间的差异。 如果差值小于或等于该绝对值,则数字相等。 如果某个数字更大,则数字不相等。 执行此作的一种方法是任意选择绝对值。 但是,这是有问题的,因为可接受的差异边距取决于值的大小 Single 。 第二种方法利用浮点格式的设计特征:两个浮点值的整数表示形式中的 mantissa 组件之间的差异指示分隔两个值的可能浮点值数。 例如,0.0 和 Epsilon 之间的差异是 1,因为 Epsilon 是在处理 Single 值为零时可表示的最小值。 下面的示例使用此技术比较 .33333 和 1/3,这两个值是上一个 Double 代码示例发现不相等的 Equals(Single) 值。 请注意,该示例使用 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($"{value1:R} = {value2:R}: {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 的版本,两个数字的比较可能会产生不同的结果,因为数字的内部表示形式的精度可能会改变。