Enumerable.Sum lança novo OverflowException para algumas entradas

O .NET 8 adiciona suporte à vetorização nos métodos Enumerable.Sum quando aplicável. Como efeito colateral dessa alteração, a implementação vetorizada pode alterar a ordem na qual os diferentes elementos são adicionados. Embora isso não altere o resultado final em execuções bem-sucedidas, isso pode resultar em exceções OverflowException inesperadas para determinados conjuntos de entradas patológicas.

Comportamento anterior

Considere o seguinte código:

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);
    }
}

Antes dessa alteração, o código anterior imprimia a seguinte saída:

0
0
OverflowException
OverflowException

Novo comportamento

A partir do .NET 8, o snippet de código da seção Comportamento anterior imprime a seguinte saída:

0
OverflowException
OverflowException
0

Versão introduzida

Prévia 7 do .NET 8

Tipo de alteração interruptiva

Esta é uma alteração comportamental.

Motivo da alteração

Essa alteração foi feita para aproveitar a vetorização em APIs LINQ.

Se o código for afetado pela alteração, você poderá:

  • Desabilite totalmente a vetorização em seu aplicativo definindo a variável de ambiente DOTNET_EnableHWIntrinsic como 0.

  • Escreva um método personalizado Sum que não use vetorização:

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

APIs afetadas