Я наблюдаю какое-то странное поведение в отношении результатов следующего кода:
namespace Test {
class Program {
private static readonly MethodInfo Tan = typeof(Math).GetMethod("Tan", new[] { typeof(double) });
private static readonly MethodInfo Log = typeof(Math).GetMethod("Log", new[] { typeof(double) });
static void Main(string[] args) {
var c1 = 9.97601998143507984195821336470544338226318359375d;
var c2 = -0.11209109500765944422706610339446342550218105316162109375d;
var result1 = Math.Pow(Math.Tan(Math.Log(c1) / Math.Tan(c2)), 2);
var p1 = Expression.Parameter(typeof(double));
var p2 = Expression.Parameter(typeof(double));
var expr = Expression.Power(Expression.Call(Tan, Expression.Divide(Expression.Call(Log, p1), Expression.Call(Tan, p2))), Expression.Constant(2d));
var lambda = Expression.Lambda<Func<double, double, double>>(expr, p1, p2);
var result2 = lambda.Compile()(c1, c2);
var s1 = DoubleConverter.ToExactString(result1);
var s2 = DoubleConverter.ToExactString(result2);
Console.WriteLine("Result1: {0}", s1);
Console.WriteLine("Result2: {0}", s2);
}
}
Код, скомпилированный для x64, дает тот же результат:
Result1: 4888.95508254035303252749145030975341796875
Result2: 4888.95508254035303252749145030975341796875
Но при компиляции для x86 или Any Cpu результаты отличаются:
Result1: 4888.95508254035303252749145030975341796875
Result2: 4888.955082542781383381225168704986572265625
Почему result1
остается неизменным, а result2
зависит от целевой архитектуры? Есть ли способ сделать так, чтобы result1
и result2
оставались одинаковыми в одной и той же архитектуре?
Класс DoubleConverter
взят с сайта http://jonskeet.uk/csharp/DoubleConverter.cs. Прежде чем вы скажете мне использовать decimal
, мне не нужно больше точности, мне просто нужно, чтобы результаты были последовательными. Целевой платформой является .NET 4.5.2, а тестовый проект был создан в режиме отладки. Я использую Visual Studio 2015 Update 1 RC в Windows 10.
Спасибо.
ИЗМЕНИТЬ
По предложению пользователя djcouchycouch я попытался еще больше упростить пример:
var c1 = 9.97601998143507984195821336470544338226318359375d;
var c2 = -0.11209109500765944422706610339446342550218105316162109375d;
var result1 = Math.Log(c1) / Math.Tan(c2);
var p1 = Expression.Parameter(typeof(double));
var p2 = Expression.Parameter(typeof(double));
var expr = Expression.Divide(Expression.Call(Log, p1), Expression.Call(Tan, p2));
var lambda = Expression.Lambda<Func<double, double, double>>(expr, p1, p2);
var result2 = lambda.Compile()(c1, c2);
x86 или AnyCpu, Отладка:
Result1: -20.43465311535924655572671326808631420135498046875
Result2: -20.434653115359243003013034467585384845733642578125
x64, Отладка:
Result1: -20.43465311535924655572671326808631420135498046875
Result2: -20.43465311535924655572671326808631420135498046875
x86 или AnyCpu, выпуск:
Result1: -20.434653115359243003013034467585384845733642578125
Result2: -20.434653115359243003013034467585384845733642578125
x64, выпуск:
Result1: -20.43465311535924655572671326808631420135498046875
Result2: -20.43465311535924655572671326808631420135498046875
Дело в том, что результаты варьируются между Debug, Release, x86 и x64, и чем сложнее формула, тем больше вероятность того, что она вызовет большие отклонения.
struct { float F; }
и вспомогательный метод, направляющий через эту структуру. 20.11.2015