Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Объясните, пожалуйста, получаемый результат

88K
29 апреля 2013 года
freeone31
3 / / 29.04.2013
Добрый день. Пытался закодить один алгоритм, не важно какой, в с++ билдере - не получилось. Потом попробовал проделать тоже самое в джаве, результат тот же. Сначала код (с++):

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 ? Покопал в инете, там писалось про числа с плавающей точкой, из-за способа их представления, это так? Можете на пальцах объяснить?
416
29 апреля 2013 года
MaitreDesir
380 / / 02.01.2008
Эта проблема "кроссплатформенна", и никак не связана с языком. Все дело, как ты правильно подозреваешь, в особенностях хранения. Подробности тут:
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?
392
29 апреля 2013 года
cronya
421 / / 03.01.2009
а что вам мешает указать кол-во знаков после запятой)) или использовать int например, он как раз округлит вам до нужного значения
88K
29 апреля 2013 года
freeone31
3 / / 29.04.2013
Я пробовал сначала инты, он зараза до 8 округляет, отбрасывает дробную часть. Можно конечно использовать математическое округление. Но вопрос не в этом, я именно хотел понять, почему так происходит. Четыре числа нормально, пятое - нет.
446
29 апреля 2013 года
Meander
487 / / 04.09.2011
У меня Dev-C++ 5.4.0 с double погрешности нет, а с float есть. Возможно дело в версии компилятора, но я немного изменил Ваш код. Конкретно по вопросу: число double может хранить всего 15 разрядов, если точное вычисление предполагает операции с разрядами выходящими из этого диапазона, то они усекаются, что и приводит к ошибке результата.
Код:
#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;
}
Вот для float

Вот для double
416
29 апреля 2013 года
MaitreDesir
380 / / 02.01.2008
Как то упустил, вот еще одна ссылочка, для лучшего понимания природы ошибки Машинное эпсилон
326
30 апреля 2013 года
sadovoya
757 / / 19.11.2005
Вот такой-же пример. Код на С++, почти аналогичный (в явном виде забил 1 как вещественное):

Код:
#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;
}
Выдает все без округления вплоть до точности PREC = 15. Начиная с PREC = 16 "портит" последнюю N.
Насчет 15 знаков у double -- это то, что писал Meander. Все так и получается.
88K
30 апреля 2013 года
freeone31
3 / / 29.04.2013
Спасибо большое всем ответившим, особенно MaitreDesir. Прочитал все внимательно, посмотрел все выложенные примеры) В http://www.delphikingdom.com/asp/viewitem.asp?catalogID=374 очень хорошо расписано, даже мой случай есть. Непонятно только, как теперь зная это избежать этих трудностей в дальнейшем? Сейчас это была простенькая лаба, и я легко нашел, где косяк. А если будет что-то серьезное?
326
01 мая 2013 года
sadovoya
757 / / 19.11.2005
Полезный ресурс по конвертации: здесь. Попробуйте там ввести десятичное число 0.1 в разных типах и посмотрите наиболее точное его представление (число 0.1 в двоичном виде -- бесконечная дробь, т.е. априори будет усекаться).
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог