Объясните, пожалуйста, получаемый результат
double a, b;
double e[5];
double N[5];
a = -6;
b = -4.
e[0] = 0.1;
e[1] = 0.01;
e[2] = 0.001;
e[3] = 0.0001;
e[4] = 0.00001;
for (int i = 0; i < 5; i++)
N[ i] = (((b - a) / e[ i]) - 1);
То есть, грубо говоря, каждый N = 2/e - 1. Теперь скрин результатов из netbeans (джава):
Ну а теперь вопрос, почему последний N так странно считается? Почему он не 199999 ? Покопал в инете, там писалось про числа с плавающей точкой, из-за способа их представления, это так? Можете на пальцах объяснить?
http://en.wikipedia.org/wiki/Floating_point
http://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html
http://www.delphikingdom.com/asp/viewitem.asp?catalogID=374
Все дело в том, что для записи "длинных" вещественных чисел, у которых длина в пределе стремится к бесконечности, необходима бесконечная память. Поэтому для реализации работы с действительными числами и были придуманы некоторые ограничения (арифметика "одинарной" точности, смотри первую ссылку).
Для хранения любого действительного числа выделяется область памяти из трех частей - бит под знак (sign), несколько бит под экспоненту (e), несколько бит под мантиссу (m). Таким образом, любое число может быть представлено как (sign) m * 10^e. Соответственно, и мантисса, и экспонента, в виду конечности памяти, имеют предел. Если длинна экспоненты влияет, в основном, на порядок диапазона возможных значений, то длинна мантиссы определяет точность вычисления значащих знаков. Следует не забывать, что не только исходный и конечный результат хранятся в таком виде.
Например, у тебя в выражении есть минимум два промежуточных результата. Кроме того, ошибка при вычислениях возможна при неявном приведении типов (в твоем выражении, ЕМНИП, 1 трактуется как int). Почему возникает только на пятом - потому, что при все большем росте порядка числа (10^-5) при промежуточных вычислениях где то округляется последний битик мантиссы, который в более коротких случаях влезал в отведенное место.
Вопрос только один меня интересует - изменится ли ситуация, если указать для единички явно тип? -1.0?
а что вам мешает указать кол-во знаков после запятой)) или использовать int например, он как раз округлит вам до нужного значения
Я пробовал сначала инты, он зараза до 8 округляет, отбрасывает дробную часть. Можно конечно использовать математическое округление. Но вопрос не в этом, я именно хотел понять, почему так происходит. Четыре числа нормально, пятое - нет.
Код:
#include <iostream>
#include "stdlib.h"
//typedef float Real;
typedef double Real;
//typedef long double Real;
int main(int argc, char *argv[]) {
Real a, b, e[5], N[5];
a = -6.0;
b = -4.0;
e[0] = 0.1;
e[1] = 0.01;
e[2] = 0.001;
e[3] = 0.0001;
e[4] = 0.00001;
for (int i = 0; i < 5; i++)
N[i] = (((b - a) / e[i]) - 1.0);
std::cout.setf( std::ios::fixed, std::ios::floatfield );
std::cout.precision(5);
std::cout << "a = " << a << " " << "b = " << b << std::endl;
for (int i = 0; i < 5; i++)
std::cout << "E" << (i+1) << " = " << e[i] << " ";
std::cout << std::endl;
for (int i = 0; i < 5; i++)
std::cout << "N" << (i+1) << " = " << N[i] << " ";
std::cout << std::endl;
system("pause");
return 0;
}
#include "stdlib.h"
//typedef float Real;
typedef double Real;
//typedef long double Real;
int main(int argc, char *argv[]) {
Real a, b, e[5], N[5];
a = -6.0;
b = -4.0;
e[0] = 0.1;
e[1] = 0.01;
e[2] = 0.001;
e[3] = 0.0001;
e[4] = 0.00001;
for (int i = 0; i < 5; i++)
N[i] = (((b - a) / e[i]) - 1.0);
std::cout.setf( std::ios::fixed, std::ios::floatfield );
std::cout.precision(5);
std::cout << "a = " << a << " " << "b = " << b << std::endl;
for (int i = 0; i < 5; i++)
std::cout << "E" << (i+1) << " = " << e[i] << " ";
std::cout << std::endl;
for (int i = 0; i < 5; i++)
std::cout << "N" << (i+1) << " = " << N[i] << " ";
std::cout << std::endl;
system("pause");
return 0;
}
Вот для double
Как то упустил, вот еще одна ссылочка, для лучшего понимания природы ошибки
Код:
#include <iostream>
using namespace std;
int main() {
double a, b;
double e[5];
double N[5];
a = -6.0;
b = -4.0;
e[0] = 0.1;
e[1] = 0.01;
e[2] = 0.001;
e[3] = 0.0001;
e[4] = 0.00001;
const int PREC = 15;
cout.setf(ios::scientific);
cout.precision(PREC);
for (int i = 0; i < 5; i++) {
N[i] = (((b - a) / e[i]) - 1.0);
cout << "N[" << i <<"] = " << N[i] << endl;
}
return 0;
}
using namespace std;
int main() {
double a, b;
double e[5];
double N[5];
a = -6.0;
b = -4.0;
e[0] = 0.1;
e[1] = 0.01;
e[2] = 0.001;
e[3] = 0.0001;
e[4] = 0.00001;
const int PREC = 15;
cout.setf(ios::scientific);
cout.precision(PREC);
for (int i = 0; i < 5; i++) {
N[i] = (((b - a) / e[i]) - 1.0);
cout << "N[" << i <<"] = " << N[i] << endl;
}
return 0;
}
Насчет 15 знаков у double -- это то, что писал Meander. Все так и получается.
Спасибо большое всем ответившим, особенно MaitreDesir. Прочитал все внимательно, посмотрел все выложенные примеры) В http://www.delphikingdom.com/asp/viewitem.asp?catalogID=374 очень хорошо расписано, даже мой случай есть. Непонятно только, как теперь зная это избежать этих трудностей в дальнейшем? Сейчас это была простенькая лаба, и я легко нашел, где косяк. А если будет что-то серьезное?
здесь. Попробуйте там ввести десятичное число 0.1 в разных типах и посмотрите наиболее точное его представление (число 0.1 в двоичном виде -- бесконечная дробь, т.е. априори будет усекаться).
Полезный ресурс по конвертации: