Как воспроизвести проблему двойного округления в C ++ - PullRequest
3 голосов
/ 20 марта 2011

Точность с плавающей запятой ограничена в C++ и для начинающих программистов, которая часто вызывает проблемы при округлении значений.

При обучении студентов часто полезно продемонстрировать проблему округления чисел с плавающей запятой.Какие возможные способы вы знаете, чтобы последовательно демонстрировать такую ​​проблему округления на всех C++ компиляторах?

Ответы [ 5 ]

5 голосов
/ 20 марта 2011

Вы можете использовать этот пример:

#include <iostream>
using namespace std;

int main() {
  for (double d = 0.0; d != 1.0; d += 0.1)
    cout << d << "\n";
}

Программа никогда не завершится, так как d никогда не будет равно 1.

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

Во-первых, мы должны отметить, что в плавающей точке IEEE754 1.5, 0.5 и 2.0 все представлены в точности.Таким образом, в этом конкретном примере 1.5 никогда не будет 1.499999999999.

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

Вот пример программы:

#include <iostream>
#include <iomanip>

int main() {
   std::cout << std::setprecision(30);
   double d1(1.1);
   std::cout << d1 << "\n";
   double d2(11);
   double eps = d2/10 - d1;
   std::cout << d2 << "\n";
   std::cout << eps << "\n";
   bool equal = (d1 == d2);
   std::cout << equal << "\n";
}

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

Для продвинутых студентов вы можете пройти дробную двоичную арифметику и понять, почему 1/2 представимо, а 1/10 нет.

РЕДАКТИРОВАТЬ: Я думаю, что путьСуть в том, чтобы сравнить повторяющиеся десятичные дроби с повторяющимися двоичными дробями.Покажите своим ученикам 1/7 в десятичном виде.Делайте длинные деления на доске.Укажите, что вы не можете записать 1/7 точно, используя ограниченные ресурсы.Затем либо покажите им, как записать 1/10 в виде двоичной дроби, либо просто скажите им, что вы не можете записать это либо с использованием конечных ресурсов.

Укажите, что числа с плавающей точкой конечны (32 бита) идвойные числа конечны (64 бита).Возможно, представьте принцип голубя и скажите, что вы не можете представить бесконечное множество (как все рациональные числа) в конечной длине слова.

Что бы вы ни пытались, сообщите об этом здесь и дайте нам знать, как это работает.

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

Мне нравится следующий пример:

double sum = 0;
for (int i = 0; i < 10; i++, sum += 0.1);
cout << "sum = " << sum << endl;
cout << "(sum == 1.) is " << boolalpha << (sum == 1.)  << endl;

Вывод следующий:

sum = 1
(sum == 1.) is false

Причина противоречия - вычисления с плавающей запятой.

1 голос
/ 20 марта 2011

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

#include <cstdio>
int main() {
    printf("%.20lf rounded to two decimal places is %.2lf\n", 0.075, 0.075);
    return 0;
}

, который печатает

0.07499999999999999722 rounded to two decimal places is 0.07

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

1 голос
/ 20 марта 2011
printf("%.20f\n",0.1f);

или

cout << fixed << setprecision(20) << 0.1f << endl;

:)

...