Непоследовательный результат из-за переупорядочения уравнений и чисел с плавающей запятой - PullRequest
0 голосов
/ 20 декабря 2018
c = .1
e = .3


(200 - 100) / (200 - 100) * (e - c) + c == .3

вывод:

[1] TRUE

Но:

(e - c) * (200 - 100) / (200 - 100) + c == .3

вывод:

[1] FALSE

Почему переупорядочение изменяет выход?

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

Я подозреваю, что это может быть оптимизация компиляции, которая вызывает различия в результатах.

В первом уравнении оба с будут просто отменять, не выполняя арифметику внутри части (e - c).

В то время как во втором уравнении арифметика внутри (e - c) должна бытьтаким образом, компьютер должен вычислить .3 - .1, что приводит к ошибке неточности.

Ответы [ 2 ]

0 голосов
/ 20 декабря 2018

Следуя примеру @ AkselA немного дальше, убрав +c для простоты:

r1 <- a / b * (e - c)
r2 <- (e - c) * a / b
r3 <- (e - c) * (a / b)
options(digits=22)
r1
## [1] 0.1999999999999999833467
r2
## [1] 0.2000000000000000111022
r3
## [1] 0.1999999999999999833467

Мы могли бы упростить это еще дальше до d <- e-c; a/b*d == d*a/b и получить те же результаты.

Результаты зависят от того, выполняется ли деление на b до умножения на (e-c) (r1, r3) или после (r2).Поскольку арифметика с плавающей точкой является коммутативной, но не ассоциативна (см. Википедия или любая из ссылок в этого ответа ), мы можем видеть, что r1 иr3 действительно должно быть идентичным (a/b и e-c оцениваются, затем умножаются), не обязательно совпадают с r2 (e-c умножается на a, тогда (e-c)*a делится на b).

  • эти различия не в отношении оптимизации компилятора, как предложено OP (R - интерпретируемый язык, он не оптимизирует выполнение арифметических выражений) [такжеаннулирование, предложенное OP, на самом деле не работает ...]
  • они , а не о целочисленных и операций с плавающей запятой (str(a) или storage.mode(a) показывают, что a - это число с плавающей запятой, а не целое число; используйте 100L, если вы хотите целое число)
0 голосов
/ 20 декабря 2018

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

Уравнение 1 и У.2 совпадают с ОП, а У.3 эквивалентно Уравнению 1 благодаря предпочтению оператора (см. ?Syntax).* и / оцениваются до + и -, за исключением того, что они оцениваются слева направо, а круглые скобки разрешаются от внутреннего к внешнему.Таким образом, в уравнениях 1 и 3 порядок составляет - / * +, а в уравнении 2 - - * / +.

a <- 100
b <- 100
c <- 0.1
e <- 0.3

r1 <- a / b * (e - c) + c    # [1]

r2 <- (e - c) * a / b + c    # [2]

r3 <- (e - c) * (a / b) + c  # [3]

r1 == r2  # FALSE

r1 == r3  # TRUE

sprintf("%.20f", c(r1, r2, r3))
# "0.29999999999999998890" "0.30000000000000004441" "0.29999999999999998890"

Понятно, что точное значение зависит от порядка операций, хотя в чисто арифметических терминах это не должно иметь значения (они ассоциативны).Но порядок имеет значение только тогда, когда вы используете некоторые конкретные значения.Если вы установите a и b на 10, скажем, или установите c на 0,2, значения будут идентичны.Я догадываюсь, что это вызвано делением, выполняемым в целочисленном режиме в уравнении (1), и режимом с плавающей запятой в уравнении (2), во втором вводятся ошибки округления, в отличие от первого (зависит от начальных значений).Разумеется, реальное сообщение о том, с чем связан Маркус , связан с , вам нужно проявлять особую осторожность при сравнении чисел с плавающей запятой, но было бы неплохо иметь определенное объяснение этого точного поведения.

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