Comparteix a través de


Enumerable.Sum produce una nueva excepción OverflowException para algunas entradas

.NET 8 agrega compatibilidad con la vectorización en los métodos Enumerable.Sum cuando corresponda. Como efecto secundario de ese cambio, la implementación vectorizada puede cambiar el orden en el que se agregan los distintos elementos. Aunque esto no debe cambiar el resultado final en ejecuciones correctas, puede dar lugar a excepciones OverflowException inesperadas para determinados conjuntos de entradas patológicas.

Comportamiento anterior

Observe el código siguiente:

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 de este cambio, el código anterior imprimió la siguiente salida:

0
0
OverflowException
OverflowException

Comportamiento nuevo

A partir de .NET 8, el fragmento de código de la sección Comportamiento anterior imprime la siguiente salida:

0
OverflowException
OverflowException
0

Versión introducida

.NET 8 Versión preliminar 7

Tipo de cambio importante

Este es un cambio de comportamiento.

Motivo del cambio

Este cambio se realizó para aprovechar la vectorización en las API de LINQ.

Si el código se ve afectado por el cambio, puede:

  • Deshabilitar la vectorización por completo en la aplicación estableciendo la variable de entorno DOTNET_EnableHWIntrinsic en 0.

  • Escribir un método personalizado Sum que no use la vectorización:

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

API afectadas