нет ли ошибки с плавающей запятой на a / b == ka / kb? - PullRequest
8 голосов
/ 10 мая 2019

Вот мой простой код.

int num1, num2;
cin >> num1 >> num2;

int num3, num4;
cin >> num3 >> num4;

double result1 = static_cast<double>(num1) / num2;
double result2 = static_cast<double>(num3) / num4;

cout.setf(ios::boolalpha);
cout << (result1 == result2) << endl;

Ввод:

1 3
2 6

Выход:

true

Итак, я хочу знать, что

static_cast<double>(a) / b == static_cast<double>(k * a) / (k * b)

всегда верно?

, если нет,

int num1, num2;
cin >> num1 >> num2;

int num3, num4;
cin >> num3 >> num4;

int gcd1 = gcd(num1, num2);
int gcd2 = gcd(num3, num4);

double result1 = static_cast<double>(num1 / gcd1) / (num2 / gcd1);
double result2 = static_cast<double>(num3 / gcd2) / (num4 / gcd2);

cout.setf(ios::boolalpha);
cout << (result1 == result2) << endl;

всегда печатается true на входе a, b, k * a, k * bна номер1, номер2, номер3, номер4?

Ответы [ 3 ]

7 голосов
/ 10 мая 2019

Предполагая, что двоичная арифметика IEEE-754 используется с правилом округления до ближайшего числа к четному, сравнение верно, за исключением следующих случаев.

Дано int num1, num2, num3 и num4, где num3 = k num1 и num4 = k num2 для некоторого действительного числа k , существуют следующие ситуации, в которых static_cast<double>(num1) / num2 == static_cast<double>(num3) / num4 может принимать значение false:

  • num3 и num4 оба равны нулю, либо потому, что num1 и num2 равны нулю, либо k равно нулю. Тогда static_cast<double>(num3) / num4 оценивается как NaN, и NaN никогда не сравнивается равным ни с чем, даже с тем же NaN.
  • num2 - ноль, но num1 - нет, а k - отрицательно. Затем static_cast<double>(num1) / num2 оценивается как + ∞ или -∞ в зависимости от того, является ли num1 положительным или отрицательным, в то время как static_cast<double>(num3) / num4 оценивает к противоположному, -∞ или + ∞ соответственно, поэтому сравнение оценивается как ложное.
  • Когда int, исключая знаковый бит, шире, чем значение и double, коэффициенты могут отличаться из-за различных округлений при преобразовании в double. Например, int может быть 64-битным, а double имеет 53-битное значение. Предположим, что num1 равно 2 53 + 1, num2 равно 1, а k равно 3, поэтому num3 равно 2 54 + 2 53 + 2 + 1 и num4 равно 3. Затем из-за округления static_cast<double>(num1) производит 2 53 , static_cast<double>(num3) производит 2 54 + 2 53 + 4, и подразделения производят 2 53 и 2 53 + 2, которые не равны.
  • В случаях, когда k num1 или k num2 переполняет тип int, сравнение может быть оценено как ложное.

За исключением случаев, приведенных выше, преобразования в double являются точными, а коэффициенты математически определены (не имеют делителей нуля) и равны. В этих случаях правило округления требует, чтобы два деления давали одинаковый результат, поэтому сравнение оценивается как истинное.

2 голосов
/ 10 мая 2019

Да, вы можете получить разные ответы; даже когда нет никаких значений NaN / Infinity и т. д. Смотрите фантастический ответ Эрика как обычно для деталей. Вот конкретный контрпример для иллюстрации:

#include <stdint.h>
#include <stdio.h>

int main()
{
    int32_t k = 1097303040;
    int32_t a = 536913409;
    int32_t b = 2097152;

    double  lhs = static_cast<double>(a)   / b;
    double  rhs = static_cast<double>(k*a) / (k*b);

    printf ("k    = %d\n", k);
    printf ("a    = %d\n", a);
    printf ("b    = %d\n", b);
    printf ("lhs  = %f\n", lhs);
    printf ("rhs  = %f\n", rhs);
    printf ("equal: %d\n", lhs == rhs);
    return 0;
}

При запуске эта программа выдает:

k    = 1097303040
a    = 536913409
b    = 2097152
lhs  = 256.020264
rhs  = -0.757798
equal: 0

Обратите внимание, что результаты не только не равны, они даже имеют разные знаки!

0 голосов
/ 10 мая 2019

Давайте предположим, что в настоящее время обычная реализация:

  • Арифметика IEEE-754
  • int 32-битный
  • double - 64-разрядный двоичный код с плавающей запятой

С этим, оба всегда верны, потому что:

  • int можно преобразовать в double без потери точности.
  • IEEE-754 обязывает это подразделение давать наилучший возможный результат, правильно округленный. Таким образом, оба деления должны возвращать одно и то же значение, сравнение не может дать false.

Обратите внимание, что это верно только в том случае, если ваша программа не содержит неопределенного поведения. Это может произойти, например, если ваша программа содержит деление на ноль. В этом случае ваше сравнение может привести к ложному. Или генерируется системное исключение (поэтому сравнение вообще не будет выполнено).

...