простой расчет, разные результаты в C # и Delphi - PullRequest
8 голосов
/ 29 апреля 2009

Вопрос в том, почему эти фрагменты кода дают разные результаты?

private void InitializeOther()
{
  double d1, d2, d3;
  int i1;

  d1 = 4.271343859532459e+18;
  d2 = 4621333065.0;
  i1 = 5;

  d3 = (i1 * d1) - Utils.Sqr(d2);
  MessageBox.Show(d3.ToString());
}

и

procedure TForm1.InitializeOther;
var d1, d2, d3 : Double;
    i1 : Integer;
begin
    d1:=4.271343859532459e+18;
    d2:=4621333065.0;
    i1:=5;

    d3:=i1*d1-Sqr(d2);
    ShowMessage(FloatToStr(d3));
end;

Код Delphi дает мне 816, а код c # - 0. Используя калькулятор, я получаю 775. Кто-нибудь может дать мне подробное объяснение?

Большое спасибо!

Ответы [ 8 ]

12 голосов
/ 29 апреля 2009

Delphi хранит промежуточные значения как расширенные (80-битный тип с плавающей запятой). Это выражение расширено:

i1*d1-Sqr(d2);

То же самое может не относиться к C # (я не знаю). Дополнительная точность может иметь значение.

11 голосов
/ 29 апреля 2009

Обратите внимание, что вы находитесь в пределах точности типа данных Double здесь, что означает, что вычисления здесь не будут точными.

Пример:

d1 = 4.271343859532459e+18

, что можно сказать так же, как:

d1 = 4271343859532459000

и так:

d1 * i1 = 21356719297662295000

на самом деле значение в .NET будет выглядеть примерно так:

2.1356719297662296E+19

Обратите внимание на округление там. Следовательно, на этом уровне вы не получаете правильных ответов.

5 голосов
/ 29 апреля 2009

Это, конечно, не объяснение этой точной ситуации, но это поможет объяснить проблему.

Что должен знать каждый компьютерщик об арифметике с плавающей точкой

4 голосов
/ 29 апреля 2009

C # double имеет не более 16 цифр точности. Взяв 4.271343859532459e + 18 и умножив на 5, получим число из 19 цифр. Вы хотите иметь номер только с 3 цифрами в результате. Двойник не может этого сделать.

В C # тип Decimal может обрабатывать этот пример - если вы знаете, что для инициализации десятичных значений используется формат 123M.

    Decimal d1, d2, d3; 
    int i1; 
    d1 = 4.271343859532459e+18M;
    d2 = 4621333065.0M;
    i1 = 5; 
    d3 = (i1 * d1) - (d2*d2); 

    MessageBox.Show(d3.ToString());

Это дает 775,00, который является правильным ответом.

3 голосов
/ 29 апреля 2009

Любой такой расчет приведет к драме с типичной арифметикой с плавающей запятой. Чем больше разница в масштабировании чисел, тем выше вероятность возникновения проблемы с точностью.

http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems дает хороший обзор.

0 голосов
/ 29 апреля 2009

Как прокомментировали другие, двойное не достаточно точно для вашего вычисления. Десятичная дробь - хорошая альтернатива, хотя кто-то указал, что она будет округлена, но это не так.

В C # тип Decimal также не может легко обработать этот пример, поскольку 4.271343859532459e + 18 будет округлено до 4271343859532460000.

Это не тот случай. Ответ, если вы используете десятичную, будет правильным. Но, как он сказал, диапазон отличается.

0 голосов
/ 29 апреля 2009

По сути, как отмечали другие люди, двойной точности недостаточно для масштаба вычислений, которые вы пытаетесь выполнить. По умолчанию Delphi использует «расширенную точность», которая добавляет еще 16 бит по сравнению с Double, чтобы обеспечить более точные вычисления. Платформа .NET не имеет типа данных расширенной точности.

Не уверен, какой тип использует ваш калькулятор, но он явно отличается от Delphi и C #.

0 голосов
/ 29 апреля 2009

Я думаю, что это ошибка, вызванная ограниченной точностью (прежде всего потому, что вместо числа используются целые числа). Возможно, d1 не такой же, как после задания. d2 * d2 наверняка будет отличаться от правильного значения, так как оно больше 2 ^ 32.

Поскольку 5 * d1 даже больше 2 ^ 64, даже использование 64-битных целых чисел не поможет. Чтобы получить правильный результат, вам придется использовать bignums или 128-битный целочисленный класс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...