Enumerable.Sum 对某些输入引发新的 OverflowException

.NET 8 在适用的 Enumerable.Sum 方法中添加了对矢量化的支持。 作为该更改的副作用,矢量化实现可以更改添加不同元素的顺序。 虽然这不应更改成功运行的最终结果,但它可能会导致某些病理输入集出现意外 OverflowException 异常。

以前的行为

请考虑以下代码:

Test(GetEnumerable1());           // Non-vectorizable
Test(GetEnumerable1().ToArray()); // Vectorizable
Test(GetEnumerable2());           // Non-vectorizable
Test(GetEnumerable2().ToArray()); // Vectorizable

static IEnumerable<int> GetEnumerable1()
{
    for (int i = 0; i < 32; ++i)
    {
        yield return 1_000_000_000;
        yield return -1_000_000_000;
    }
}

static IEnumerable<int> GetEnumerable2()
{
    for (int i = 0; i < 32; ++i)
    {
        yield return 100_000_000;
    }
    for (int i = 0; i < 32; ++i)
    {
        yield return -100_000_000;
    }
}

static void Test(IEnumerable<int> input)
{
    try
    {
        Console.WriteLine(input.Sum());
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.GetType().Name);
    }
}

在进行此更改之前,前面的代码输出了以下输出:

0
0
OverflowException
OverflowException

新行为

从 .NET 8 开始, “上一行为 ”部分中的代码片段将输出以下输出:

0
OverflowException
OverflowException
0

已引入的版本

.NET 8 预览版 7

破坏性变更的类型

此更改为行为更改

更改原因

此更改旨在利用 LINQ API 中的向量化。

如果代码受更改影响,则可以:

  • 通过将环境变量设置为 DOTNET_EnableHWIntrinsic 0,在应用程序中完全禁用矢量化。

  • 编写不使用矢量化的自定义 Sum 方法:

    static int Sum(IEnumerable<int> values)
    {
        int acc = 0;
        foreach (int value in values)
        {
            checked { acc += value; }
        }
        return acc;
    }
    

受影响的 API