flash_back
@flash_back

Упражнение из книги Страуструпа. Программа про зерна риса и шахматную доску. Как все таки выполнить задание корректно?

Всем привет.
Сделал упражнения к книге Бьерна Страуструпа "Программирование. Принципы и практика
использования С++".
Текст упражнений такой:
ba953bd45bcc4d989cd3f707d19589c9.png
Мое решение для этих упражнений такое:
#include <iostream>
#include <cmath>
#include <limits> 

using namespace std;

#define LOG_STEPS

void how_cells(double seeds)
{
	double sum = 0;
	int cell_numb=0;
	double seed_count=0;
	for ( cell_numb=1, seed_count=1.0; 
		                cell_numb <= 64;
						cell_numb++,seed_count*=2.0
		)
	{
		
		sum += seed_count;

		#ifdef LOG_STEPS
			cout << cell_numb << "  " << fixed << seed_count << "  " << fixed << sum <<  endl;
		#endif //LOG_STEPS

		if (sum>=seeds)
		{
			cout << "Answer is " << cell_numb << endl;
			break;
		}
	}
}

int main()
{
	//До какой клетки дойдем, чтобы получить хотя бы 1000 зерен риса в сумме
	cout << "Task#1. Cell numb if 1000 seeds:" << endl;
	how_cells(1000);
	//До какой клетки дойдем, чтобы получить хотя бы 1000000 зерен риса в сумме 
	cout << "Task#2. Cell numb if 1.000.000 seeds:" << endl;
	how_cells(1000000);
	//До какой клетки дойдем, чтобы получить хотя бы 1000000000 зерен риса в сумме
	cout << "Task#3. Cell numb if 1.000.000.000 seeds:" << endl;
	how_cells(1000000000);
	//До какой клетки дойдем, чтобы получить максимальное значение для int зерен риса в сумме
	cout << "Task#4. Cell numb in int max:" << endl;
	cout << "Max of int: " << numeric_limits<int>::max() << endl;
	how_cells(numeric_limits<int>::max());
	//До какой клетки дойдем, чтобы получить максимальное значение для double(только мантисса) зерен риса в сумме
	cout << "Task#5. Cell numb in double max:" << endl;
	//Должно быть такое значение 9223372036854775808.0 по замыслу Страуструпа, но младшие декады теряются
	cout << "Max of double (mantissa): " << 9223372036854775808.0 << endl; //берется только мантисса pow(2.0,63.0) != numeric_limits<double>::max()
	how_cells(9223372036854775808.0); //берется только мантисса pow(2.0,63.0) != numeric_limits<double>::max()
	system("pause");
	return 0;
}

При вычислении номера клетки для того чтобы в сумме получить 1000, 1000000, 1000000, 217483647(максимальное значение для int) все хорошо. А вот для максимального значения double не все однозначно. Явно в упражнении не говорится, но Страуструп как я понял говорит только о мантиссе и количестве знаков в ней, порядок в расчет вроде как не берется (иначе максимальное число при потере знаков в мантиссе было бы в степени 308). Поэтому если говорить о мантиссе я думаю, что подразумевалось число 9223372036854775808, т.е 63-я клетка и сумма для нее. Но по факту получаем на этой клетке 9223372036854775800. И сюдя по выводимым значениям начинается это искажение гораздо раньше на значениях 72057594037927936 больше, т.е. сумма после 56-й клетки. Но как тогда определить это искажение в программе? Можно конечно задать константу, в которой будет значение 72057594037927936, вроде оно не перетрется при записи, но думаю такое решение не до конца верно . Отсюда вопрос: какое максимальное значение мантиссы без потери знаков(младшие декады) можно задать для типа double и можно ли его узнать стандартными средствами библиотеки?

upd. Нашел средства библиотеки, которые обеспечивают вывод количества доступных цифр в мантиссе без искажений. Вот пример кода:
#include <iostream>
#include <cfloat> //DBL_DIG
#include <limits> //std::numeric_limits<double>  


using namespace std;

int main()
{

	cout << "Number of digits (in decimal base) that can be represented without change." << endl;
	cout <<  "pure C. <cfloat>. Answer: " << DBL_DIG << endl;
	cout <<  "C++. <limits>.std::numeric_limits<double>. Answer: "<< numeric_limits<double>::digits10 << endl;
	
	//Нет искажения (15 цифр)
	cout << fixed << 999999999999999.0 << endl;
	//Есть искажение по последней цифре (16 цифр)
	cout << fixed << 9999999999999999.0 << endl;
	//Нет искажения (16 цифр)
	cout << fixed << 9999999999999912.0 << endl;
	//Нет искажения (17 цифр). Наш случай макисмального неискаженного числа. 
	//Т.е. вышли за 15 цифр.
	cout << fixed << 72057594037927936.0 << endl;


	system("pause");
	return 0;
}

Получается, что для типа double это гарантировано пятнадцать цифр. Можно было бы получить это значение в программе и потом считать кол-во цифр суммы при добавлении каждой последующей клетки. Однако как видно при выполнении программы при превышении 15-ти цифр не всегда наблюдается искажение. И в нашем случае с доской искажения нет до 17-ти цифр (72057594037927936 , сумма после 56-й клетки). Получается, что определить кол-во цифр для мантиссы при которых еще не будет искажения для каждого конкретного случая (в частности для нашего) средствами библиотек C/C++ не представляется возможным. Не понятно возможно ли определить клетку и соответствующую ей сумму (клетка 56, сумма 72057594037927936 ) в автоматическом режиме а не просто сравнением с константой полученной более ранним прогоном программы. На данный момент такого варианта не вижу. Может кто-то все таки знает как?

upd Написал вариант решения задачи с типом unsigned long long (8 байт) получается вычислить количество зерен полностью (2^64). В него влезает кол-во зерен всей доски. Если бы было на одно зерно больше то не влезло бы. Решение:
#include <iostream>
#include <cmath>
#include <limits> 

using namespace std;


unsigned long long calc_sum()
{
	unsigned long long sum = 0;
	int cell_numb=0;
	unsigned long long seed_count=0;
	for ( cell_numb=1, seed_count=1; 
		                cell_numb <= 64;
						cell_numb++,seed_count*=2
		)
	{
		
		sum += seed_count;

		cout << cell_numb << "  " << seed_count << "  " << sum <<  endl;

	}

	return  sum;
}

int main()
{

	cout << "Max of unsigned long long: " << numeric_limits<unsigned long long>::max() << endl << endl;
	unsigned long long total_seed_count = calc_sum();
	cout << endl << "Total: " << total_seed_count << endl;
	system("pause");
	return 0;
}

Но это не решает поставленную Бьерном задачу с типом double.
  • Вопрос задан
  • 1321 просмотр
Пригласить эксперта
Ответы на вопрос 1
@res2001
Developer, ex-admin
Зачем тебе double? В задаче же сказано - переменная типа int. А точнее unsigned int применительно к этой задаче.
Кроме того вычислять степень в цикле - не интересно.
Задачу можно решить используя битовую арифметику. Если учесть что каждый установленный бит в unsigned int - это 2 в некоторой степени равной позиции бита в числе. Нужно найти позицию самого старшего значащего бита в числе +1 - это и будет ответом на вопрос задачи.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы