непроверенный блок не работает с BigInteger? - PullRequest
4 голосов
/ 27 августа 2011

Только что заметил, что контекст unchecked не работает при работе с BigInteger, например:

unchecked
{
    // no exception, long1 assigned to -1 as expected
    var long1 = (long)ulong.Parse(ulong.MaxValue.ToString());
}

unchecked
{
    var bigInt = BigInteger.Parse(ulong.MaxValue.ToString());

    // throws overflow exception
    var long2 = (long)bigInt;
}

Есть идеи, почему это так?Есть ли что-то особенное в том, как большие целые числа преобразуются в другие примитивные целочисленные типы?

Спасибо,

Ответы [ 3 ]

18 голосов
/ 27 августа 2011

Компилятор C # не имеет ни малейшего представления, что BigInteger логически является «целочисленным типом».Он просто видит пользовательский тип с заданным пользователем явным преобразованием в long.С точки зрения компилятора,

long long2 = (long)bigInt;

в точности совпадает с:

long long2 = someObject.SomeMethodWithAFunnyNameThatReturnsALong();

Он не может достичь внутри этого метода и сказать ему прекратить генерировать исключения.

Но когда компилятор видит

int x = (int) someLong;

, компилятор генерирует код, выполняющий преобразование, поэтому он может выбрать создание проверенного или непроверенного кода по своему усмотрению.

Помните, что «проверено» и «не проверено» не имеют никакого эффекта во время выполнения;Это не значит, что CLR переходит в «непроверенный режим», когда управление входит в непроверенный контекст.«check» и «unchecked» - это инструкции для компилятора о том, какой код генерировать внутри блока.Они действуют только во время компиляции, и компиляция преобразования BigInt в long уже произошла.Его поведение исправлено.

4 голосов
/ 27 августа 2011

OverflowException фактически генерируется явным оператором приведения, определенным в BigInteger.Это выглядит так:

int num = BigInteger.Length(value._bits);
if (num > 2)
{
    throw new OverflowException(SR.GetString("Overflow_Int64"));
}

Другими словами, он обрабатывает переполнения таким образом независимо от контекста checked или unchecked. Документы на самом деле так говорят.

Обновление : Конечно, последнее слово об Эрике.Пожалуйста, прочитайте его пост:)

2 голосов
/ 27 августа 2011

В документации прямо указано, что в этой ситуации будет выброшено OverflowException. Проверенный контекст имеет значение только для «родных» арифметических операций, которые испускает компилятор C #, что не включает в себя вызов явных операторов преобразования.

Чтобы выполнить преобразование "безопасно", вам нужно сначала сравнить его с long.MaxValue и long.MinValue, чтобы проверить, находится ли оно в диапазоне. Я подозреваю, что для получения эффекта переполнения на отрицательный сначала вам нужно будет использовать битовые операторы в пределах BigInteger. Например:

using System;
using System.Numerics;

class Program
{
    static void Main(string[] args)
    {
        BigInteger bigValue = new BigInteger(ulong.MaxValue);

        long x = ConvertToInt64Unchecked(bigValue);
        Console.WriteLine(x);
    }

    private static readonly BigInteger MaxUInt64AsBigInteger
        = ulong.MaxValue;

    private static long ConvertToInt64Unchecked(BigInteger input)
    {
        unchecked
        {
            return (long) (ulong) (input & MaxUInt64AsBigInteger);
        }
    }
}
...