Поделиться через


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)

Тип критического изменения

Это изменение поведения.

Причина изменения

Это изменение было сделано для использования векторизации в API LINQ.

Если код влияет на изменение, можно выполнить следующие действия:

  • Отключите векторизацию полностью в приложении, задав DOTNET_EnableHWIntrinsic переменную среды значение 0.

  • Напишите пользовательский Sum метод, который не использует векторизацию:

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

Затронутые API