C# List与HashSet的Remove方法如何移除浮点数元素?
C#中集合类型对浮点数的Remove操作逻辑
受机器精度限制,无法直接基于相等性做可靠的浮点数比较,C#中List、HashSet等集合类型的Remove方法运行逻辑可先通过基础实现规则推导:
List<T>底层基于数组实现,会以O(n)复杂度遍历查找目标元素,匹配到对应元素即执行移除HashSet<T>采用典型O(1)复杂度的哈希匹配逻辑:先计算传入值的哈希码定位哈希桶,再判断桶内是否存在匹配元素,存在即移除
问题1:List遍历判断两个浮点数是否相等时,是否直接使用==运算符?
从实验结果来看,该判断逻辑成立。
第一次测试代码:
var floatList = new List<float>(){0.1001f, 0.2f, 0.3f, 0.4f}; floatList.ShowAll("Before"); floatList.Remove(0.1f); floatList.ShowAll("After"); // 输出结果 //Before //0.1001 0.2 0.3 0.4 //After //0.1001 0.2 0.3 0.4 //0.1001 未被移除
将第一个元素修改为0.1000000001f后,运行结果如下:
//Before //0.1000000001 0.2 0.3 0.4 //After //0.2 0.3 0.4 //0.1000000001f 被移除
注:第二个测试中传入的0.1000000001f超出了float类型的精度表示范围,实际存储的二进制值和0.1f完全一致,因此会被判定为相等并移除,这和==运算符的判断表现完全吻合。
问题2:若Remove方法默认使用==比较浮点数,是否可以修改比较逻辑?
将集合替换为HashSet<float>做相同测试时,表现和List<float>完全一致,这符合.NET集合的设计规则:类型的GetHashCode()返回值必须和Equals()的判断结果逻辑自洽,因此哈希结构中浮点数的比较精度和==表现完全相同。
如果需要自定义比较逻辑(比如允许指定误差范围内的近似相等),不需要修改浮点数类型本身的相等判断,可以通过以下方式实现:
- 对
List<T>:其Remove方法没有传入自定义比较器的重载,可以使用RemoveAll()方法传入自定义匹配谓词,按自定义精度规则匹配并移除元素 - 对
HashSet<T>、Dictionary<TKey, TValue>这类哈希结构:直接在构造函数中传入自定义的IEqualityComparer<T>实现,后续的Add、Remove、Contains等所有操作都会按自定义的相等规则执行
问题3:C#中float类型的哈希值是如何计算的?
float(即System.Single)的GetHashCode()实现会直接返回浮点数二进制位表示对应的整型值,只有两个浮点数的二进制表示完全一致时,才会返回相同的哈希值,和==、Equals()的判断逻辑完全对齐。需要注意0.0f和-0.0f的二进制表示不同,返回的哈希值也不同;NaN的特殊二进制表示也会返回固定的对应哈希值。
本次测试使用的扩展方法代码
public static class IEnumerableExt { public static void ShowAll<T>(this IEnumerable<T> iEnumerable, string title) { Console.WriteLine(title); foreach (var ele in iEnumerable) { Console.Write(ele + " "); } Console.WriteLine(); } }
内容的提问来源于stack exchange,提问作者Reimirno
相关产品推荐
相关产品推荐

