2009-07-28 7 views
2

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

double _west = 9.482935905456543; 
double _off = 0.00000093248155508263153; 
double _lon = _west + _off; 

// check for the expected result 
Debug.Assert(_lon == 9.4829368379380981); 
// sometimes i get 9.48293685913086 for _lon (which is wrong) 

Я использую некоторые собственные библиотеки DLL в моем приложении, и я подозреваю, что некоторые DLL несет ответственность за этот «просчет», но мне нужно выяснить, какой из них. Может ли кто-нибудь дать мне подсказку, как выяснить корень моей проблемы?

+0

То, что я говорю здесь, относится к удваивать, а также: http://stackoverflow.com/questions/ 1193554/problem-with-float-in-objective-c/1193607 # 1193607 –

+0

Корень проблемы вызван неправильной настройкой точности с плавающей запятой. Кто-то устанавливает точность с плавающей точкой в ​​24 бита, и это приводит к неправильному вычислению. Использование _fpreset или _controlfp (из DLL среды выполнения MSVC) может исправить это, но по-прежнему остается загадкой, которая устанавливает эту точность в первую очередь? –

ответ

9

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

В advanteage использования двойного и плывет над десятичной производительностью

+1

-1 Как «Десятичный» может повысить производительность над «Двойным»? Ваш процессор знает, как обрабатывать 'Singles' и' Doubles', но все операции с 'Decimal' должны происходить в памяти медленнее. –

+2

Возможно, мой ответ был немного неясным, но это то, что я имел в виду – terjetyl

+3

@TT. Я не знаю, как я это сделал, но я полностью неправильно понял ваш ответ. Извиняюсь! (-1) и +1 для указания на производительность! –

2

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

Попробуйте это:

using System; 

class Program 
{ 
    static void Main() 
    { 
     double _west = 9.482935905456543; 
     double _off = 0.00000093248155508263153; 
     double _lon = _west + _off; 

     // check for the expected result 
     Console.WriteLine(_lon == 9.48293683793809808263153);  
    } 
} 

В будущем, хотя лучше использовать System.Decimal в тех случаях, когда вам необходимо, чтобы избежать ошибок округления, которые обычно ассоциируются с System.Single и System.Double типов.

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

+0

Я попытался использовать десятичные числа, но преобразование двойного в десятичный не удалось, потому что десятичная запятая автоматически округляла заданное значение. Как этого можно избежать? –

+1

+1 Я полностью пропустил неправильное утверждение! – n8wrl

0

Вас укушают проблемы округления и точности. См. this. Decimal может помочь. Перейдите к here для получения подробной информации о конверсиях и округлении.

Из MSDN:

При преобразовании с плавающей точкой или дважды в десятичное значение источника преобразуется в десятичное представление и округляется до ближайшего числа после 28-го знака после запятой, если это необходимо. В зависимости от значения источника может произойти один из следующих результатов:

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

Если исходное значение - NaN (не число), бесконечность или слишком большое, чтобы быть представленным как десятичное число, генерируется исключение OverflowException.

0

Проблема заключается в том, что Double имеет точность 15-16 знаков (и вам кажется, что в вашем примере требуется более высокая точность), тогда как Decimal имеет точность до 28-29. Как вы конвертируете между Double и Decimal?

0

Вы не можете точно представлять каждое число с плавающей запятой в десятичной системе с плавающей точкой в ​​двоичной системе, это даже не связано напрямую с тем, как «маленький» десятичный номер, некоторые цифры просто «не подходят» »в базе-2 красиво.

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

Чтобы задать свои константы в Decimal (с плавающей точкой 128-бит) точности, используйте эту декларацию:

decimal _west = 9.482935905456543m; 
decimal _off = 0.00000093248155508263153m; 
decimal _lon = _west + _off;