Будьте предельно осторожны, используя любые другие предложения. Все зависит от контекста.
Я долго трассировал ошибки в системе, которая предположила a==b
, если |a-b|<epsilon
. Основными проблемами были:
a==b
и b==c
, то a==c
. a==b
, но 1000a!=1000b
. (Вот почему AlmostEqual2sComplement запрашивает epsilon или max ULPS). Как я уже сказал, все зависит от контекста и ожидаемого размера a
и b
.
BTW, std::numeric_limits<double>::epsilon()
- это «машинный эпсилон». Это разница между 1.0 и следующим значением, представляемым двойным. Я предполагаю, что он может использоваться в функции сравнения, но только если ожидаемые значения меньше 1. (Это ответ на ответ @ cdv ...)
Кроме того, если вы в основном имеете int
арифметики в doubles
(здесь мы используем двойники для хранения значений int в некоторых случаях), ваша арифметика будет правильной. Например, 4.0 / 2.0 будет таким же, как 1.0 + 1.0. Это до тех пор, пока вы не делаете то, что приводит к фракциям (4.0 / 3.0) или не выходят за пределы размера int.
Это очень странно! Здесь есть сообщение в блоге здесь , в котором описывается, как изменилось поведение Cast<T>()
между .NET 3.5 и .NET 3.5 SP1, но оно по-прежнему не объясняет InvalidCastException, которое вы даже получаете, если переписываете ваш код таким образом:
var list = new[] { 1 };
var castedList = from long l in list select l;
Console.WriteLine(castedList.First());
Очевидно, вы можете обойти это, сделав бросок самостоятельно
var castedList = list.Select(i => (long)i);
Это работает, но это не объясняет ошибку в первую очередь , Я попытался сделать список коротким и плавающим, и те бросили то же исключение.
Изменить
В этом сообщении в блоге объясняется, почему он не работает!
Cast<T>()
является методом расширения на IEnumerable
, а не IEnumerable<T>
. Это означает, что к тому моменту, когда каждое значение дойдет до того момента, когда оно будет запущено, оно снова помещено в объект System.Object. По сути, он пытается это сделать:
int i = 1;
object o = i;
long l = (long)o;
Этот код генерирует InvalidCastException, которое вы получаете. Если вы пытаетесь применить int непосредственно к длинному, вы в порядке, но отбрасывание коробочного int back в long не работает.
Конечно, странность!
Конечно, разумная вещь - использовать Select(i => (long)i)
, и это то, что я бы рекомендовал для конверсий между встроенными типами значений и для пользовательского преобразования.
Но так же, как любопытный замечание, так как .NET 4 можно сделать свой собственный метод расширения, который также работает с этими видами конверсий. Но для этого требуется, чтобы вы использовали ключевое слово dynamic
. Это происходит просто так:
public static IEnumerable<TResult> CastSuper<TResult>(this IEnumerable source)
{
foreach (var s in source)
yield return (TResult)(dynamic)s;
}
Как я уже говорил, работает с интегральными преобразованиями (сужение или расширение преобразований), числовые преобразования в / из / между типами с плавающей запятой и «методы» преобразования implicit operator
и explicit operator
.
И, конечно же, он по-прежнему работает с хорошими старыми обратными преобразованиями и распаковками, такими как оригинал System.Enumerable.Cast<TResult>
.
dynamic
, поскольку анализ того, какое преобразование использовать, должно быть выполнено во время выполнения. Вот почему .Select(i => (long)i)
рекомендуется.
– Jeppe Stig Nielsen
21 November 2012 в 15:31
Метод Enumerable.Cast определяется как следующий:
public static IEnumerable<TResult> Cast<TResult>(
this IEnumerable source
)
И нет информации об исходном типе элементов IEnumerable, поэтому я думаю, что каждый из ваших ints первоначально преобразован в System.Object через бокс и затем он пытается быть распакован в длинную переменную, и это неверно.
Аналогичный код для воспроизведения:
int i = 1;
object o = i; // boxing
long l = (long)o; // unboxing, incorrect
// long l = (int)o; // this will work
Таким образом, решение для вашей проблемы будет:
ints.Select(i => (long)i)
Хм ... интересная головоломка. Тем более интересно, что я просто запустил его в Visual Studio 2008, и он вообще не выбрал .
Я не использую Service Pack 1, и вы можете быть , так что это может быть проблемой. Я знаю, что в выпуске SP1 были выпущены некоторые улучшения производительности в .Cast (), которые могут вызвать проблему. Некоторое чтение:
Вот о чем подумать ...
List<T>
или IEnumerable<T>
. IEnumerable<T>
, вы хотите, чтобы приведение cast / convert выполнялось лениво (т. е. cast / convert фактически не произойдет до тех пор, пока итератор не достигнет каждого элемента)? Полезное различие между cast / convert, поскольку оператор литья часто включает в себя создание нового объекта и может считаться преобразованием: реализации «Cast» должны автоматически применять операторы преобразования, определенные для задействованных типов; новый объект может быть или не быть сконструирован . Реализации «Преобразовать» должны позволять задавать делегат System.Converter<TInput,TOutput>
.
Потенциальные заголовки методов:
List<TOutput> Cast<TInput,TOutput>(IEnumerable<TInput> input);
List<TOutput> Convert<TInput,TOutput>(IEnumerable<TInput> input, Converter<TInput,TOutput> converter);
IEnumerable<TOutput> Cast<TInput,TOutput>(IEnumerable<TInput> input);
IEnumerable<TOutput> Convert<TInput,TOutput>(IEnumerable<TInput> input, Converter<TInput,TOutput> converter);
Проблемные реализации «Cast» с использованием существующей структуры; предположим, что вы передаете вход List<string>
, который хотите преобразовать с любым из предыдущих методов.
//Select can return only a lazy read-only iterator; also fails to use existing explicit cast operator, because such a cast isn't possible in c# for a generic type parameter (so says VS2008)
list.Select<TInput,TOutput>( (TInput x) => (TOutput)x );
//Cast fails, unless TOutput has an explicit conversion operator defined for 'object' to 'TOutput'; this confusion is what lead to this topic in the first place
list.Cast<TOuput>();
Проблемные реализации «Преобразовать»
//Again, the cast to a generic type parameter not possible in c#; also, this requires a List<T> as input instead of just an IEnumerable<T>.
list.ConvertAll<TOutput>( new Converter<TInput,TOuput>( (TInput x) => (TOutput)x ) );
//This would be nice, except reflection is used, and must be used since c# hides the method name for explicit operators "op_Explicit", making it difficult to obtain a delegate any other way.
list.ConvertAll<TOutput>(
(Converter<TInput,TOutput>)Delegate.CreateDelegate(
typeof(Converter<TInput,TOutput>),
typeof(TOutput).GetMethod( "op_Explicit", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public )
)
);
/ Преобразование методов должно включать определенные явные операторы преобразования или позволить указать делегат преобразования. Спецификация языка C # для операторов преобразования - в частности, отсутствие имени метода - затрудняет получение делегата, за исключением отражения. Альтернативой является инкапсуляция или копирование кода конверсии, что приводит к излишней (несовместимости) сложности вашего кода, так как действительно, возможные / разрешенные преобразования являются неявными в присутствии или отсутствии операторов преобразования и должны быть обрабатывается компилятором. Нам не нужно вручную искать критически названные определения (например, «op_Explicit») соответствующих операторов преобразования с отражением в RUN TIME по соответствующим типам. Кроме того, методы Cast / Convert для конверсий с массовыми / списками с использованием явных операторов преобразования должны действительно быть функцией структуры, а с List.ConvertAll<T>
они ... кроме спецификации языка затрудняют получение делегата для операторов преобразования эффективно! !!
Cast<T>
для «fix & quot; это, см. мой недавний ответ. – Jeppe Stig Nielsen 21 November 2012 в 15:32