Мы конвертируем математическую библиотеку C ++ в C #. Библиотека смешивает использование чисел с плавающей запятой и двойных чисел (иногда приводя их между собой), и мы пытаемся сделать то же самое, чтобы получить те же самые результаты в C #, которые мы имели в C ++, но это оказывается очень трудным, если не невозможным
Я думаю, что проблема в одном или нескольких из следующих, но я не эксперт:
Преобразование чисел с плавающей точкой в двойную и двойную в плавающие вызывает непредсказуемые результаты и выполняется по-разному в C ++ и C #.
C ++ и C # обрабатывают точность с плавающей точкой по-разному, и они не могут имитировать друг друга
. .NET, чтобы заставить его работать как C ++, но я не могу его найти (оба 32-разрядные)
Может кто-нибудь объяснить мне возможные проблемы и, возможно, дать мне ссылку на некоторые авторитетные документы от Microsoft, которые я могу использовать, чтобы помочь объяснить ситуация и причина различий?
РЕДАКТИРОВАТЬ
Мы используем VC6 и .NET4.0
Я не могу привести примеры расчетов из-за NDA, но я могу показать некоторые цифры для различия ... вероятно, очень бесполезны сами по себе:
8.085004000000000 (C#) vs.
8.084980000000000 (C++)
8.848165000000000 (C#) vs.
8.848170000000000 (C++)
0.015263214111328 (C#) vs.
0.015263900756836 (C++)
Следует отметить, что эти числа включают сложные проблемы. Это результаты расчетов.
C++ позволяет программе сохранять более высокую точность для временных результатов, чем тип подвыражений подразумевал бы. Одна вещь, которая может случиться, заключается в том, что промежуточные выражения (или их неопределенное подмножество) вычисляются как расширенные 80-битные числа с плавающей запятой.
С другой стороны, я был бы удивлен, если бы это применялось к C#, но даже если это так, компилятору C# не нужно выбирать то же самое подмножество выражений для вычисления, что и 80-битные расширенные числа с плавающей запятой. РЕДАКТИРОВАТЬ: см. комментарий Эрика ниже.
Еще один пример той же проблемы с промежуточной точностью, когда компилятор использует инструкцию fmadd
для умножения, за которым следует сложение в исходном коде (если в целевой архитектуре это предусмотрено). — например, PowerPC). Инструкция fmadd
точно вычисляет свой промежуточный результат, тогда как обычное сложение округляет промежуточный результат.
Чтобы предотвратить это от компилятора C++, вам нужно всего лишь написать вычисления с плавающей запятой в виде трехадресного кода с использованием volatile-переменных для промежуточных результатов. Если это преобразование изменит результат программы на C++, это означает, что возникла описанная выше проблема. Но тогда вы изменили результаты на стороне С++.Вероятно, невозможно получить те же самые старые результаты C++ в C#, не читая сгенерированную сборку.
Если он немного устарел, ваш компилятор C++ может также оптимизировать вычисления с плавающей запятой, как если бы они были ассоциативными, когда это не так. Вы мало что можете с этим поделать. Это просто неправильно. Преобразование кода с тремя адресами снова помешает компилятору применить его, но опять же нет простого способа заставить компилятор C# воспроизвести старые результаты C++.