Являются ли арифметические операции над двойными переменными, содержащими целые значения, точными? - PullRequest
14 голосов
/ 11 марта 2011

Допустим, у меня есть два целочисленных значения, хранящихся в double переменных, например:

double x = 100.0;
double y = 7.0;

Могу ли я с уверенностью предположить, что любая арифметическая операция с этими двумя двойными переменными, которая даст целочисленный результат,вернет точное целочисленное значение (как double)?То есть, например, будут ли все:

x + y = 107.0
x - y = 93.0
x * y = 700.0

возвращать точные целочисленные значения или будут некоторые проблемы с точностью?Например, x*y 699.99995 или около того?

Общий вопрос: Правда ли, что любая арифметическая операция с двумя двойными переменными, содержащими целочисленные значения, которая даст целочисленный результат, вернетточное целочисленное значение (как двойное)?

Я спрашиваю это в контексте Java, но я полагаю, что оно аналогично и в других языках.

Ответы [ 7 ]

12 голосов
/ 11 марта 2011

Пока целочисленный результат вашей операции может быть точно представлен как двойной, вы получите точный результат, но как только целочисленный результат превысит количество битов, доступных в мантиссе (то есть 52 + 1 = 53 бит), будет округлено.

4 голосов
/ 11 марта 2011

В общем, ответ - нет. Однако я настоятельно рекомендую прочитать Дэвида Голдберга "Что должен знать каждый специалист по вычислительной технике об арифметике с плавающей точкой" - никогда не повредит узнать вещи изнутри.

3 голосов
/ 11 марта 2011

Отличная дискуссия, все.

Ваш вопрос

Правда ли, что любая арифметика операция над двумя двойными переменными удерживая целочисленные значения, которые бы дать целочисленный результат вернет точное целое значение (как двойное)?

Я выбрал пограничный случай, когда два числа были ровно 53 бита. 54-битная сумма превысила двойную, и не дала точного целочисленного результата. Как и ожидалось, младший бит был обрезан, и вы получили странный, но ожидаемый результат.

Нечетное число плюс четное число не приводит к нечетной сумме (как скажет вам математика); Java сообщает четное число (как сказал бы стандарт IEEE).

Попробуйте этот образец:

private static void doubleCalc() {
  double x = 4503599627370497.0d; // binary 10000000000000000000000000000000000000000000000000001
  double y = 4503599627370496.0d; // binary 10000000000000000000000000000000000000000000000000000

  double sum = x + y;
  System.out.println("sum=" + sum + "; should be 9007199254740993.0d");
}

Будет распечатано:

sum=9.007199254740992E15; should be 9007199254740993.0d

Так что этот тщательно подобранный контрпример ответил бы «нет» на ваш тщательно сформулированный вопрос.

3 голосов
/ 11 марта 2011

Нет, если полученный номер имеет слишком много цифр, чтобы поместиться в double.Например, 1234567890.0 * 1234567890.0 дает 1,52415787501905E+18 вместо 1524157875019052100.Я не знаю, будет ли он всегда точным, если результат соответствует, но @Sven Marnach ответил на это.Я предполагаю, что усеченное число будет отключено на точное целое число, как говорит @Douglas Leeder, потому что мантисса, смещенная на показатель степени (который больше, чем число цифр в мантиссе), станет целым числом.

2 голосов
/ 11 марта 2011

Все int значения могут быть точно представлены double значениями, и операции +, *, - работают здесь одинаково (до тех пор, пока вы не превысите диапазон int ). Операторы / и % работают по-разному.

Поскольку double имеет только 52 бита мантиссы, вы также не можете точно представить все значения long.

1 голос
/ 18 февраля 2013

Пока числа не слишком далеко друг от друга (например, 2 ^ 1024 и 0,005), результаты должны быть точными. Числа с плавающей запятой двойной точности работают следующим образом: 1 бит для знака, 11 для экспоненты и 52 бита для мантиссы. Конечное число ((-1) * (знак)) (1.mantissa << (экспонента - 1 << 10)), поэтому при добавлении между двумя числами происходит следующее: </p>

x = number with greatest exponent
y = number with smallest exponent

(in case of same sign)
z.mantissa = x.mantissa + (y.mantissa >> (x.exponent - y.exponent) )
sign = either_one.sign

(in case of opposite sign)
z.mantissa = x.mantissa - (y.mantissa >> (x.exponent - y.exponent) )
sign = x.sign

для умножения / деления это немного проще:

z.exponent = x.exponent + y.exponent
z.mantissa = 1.(x.mantissa) (operand) (y.mantissa)
z.sign = x.sign != y.sign
while (z.mantissa is not in format 1.x)
   z.mantissa << 1 (division)
   z.exponent--
   z.mantissa >> 1 (multiplication)
   z.exponent++

Итак, что происходит, если показатели находятся слишком далеко друг от друга, когда произойдет сдвиг, произойдет потеря данных, что означает, что точность для двойной точности (с плавающей запятой в общем) не является точной на 100% (особенно потому, что некоторые числа превращаются в периодические опустошает). Для совершенных целых чисел (и результатов), однако, это должно быть в порядке, если число имеет длину до 52 бит (размер мантиссы), поскольку оно может быть преобразовано в целое число процессором (например, 1.111 << 3 равно 1111 ). </p>

0 голосов
/ 11 марта 2011

В связанном вопросе мне было указано, что двойной тип имеет точность около 15 цифр, в то время как он может содержать до 10^(300+) больших чисел.Так что я думаю, что пока вы используете меньший int, это не должно быть большой проблемой.

Это, как говорится, немного из уроков оракула:

double: тип данных double - 64-разрядная IEEE 754 с плавающей запятой двойной точности.Его диапазон значений выходит за рамки этого обсуждения, но указан в разделе 4.2.3 Спецификации языка Java.Для десятичных значений этот тип данных обычно является выбором по умолчанию.Как упомянуто выше, этот тип данных никогда не должен использоваться для точных значений, таких как валюта.

Для дальнейшей ссылки, здесь есть ссылка на раздел 4.2.3 , упомянутый выше.

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