![]() |
Модераторы: Daevaorn |
![]() ![]() ![]() |
|
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
В моих программах часто выполняются различные расчеты. Нередко встречаются очень маленькие цифры (например первая значащая цифра появляется в десятом знаке после запятой) и мне нужна достаточная точность, что бы 0.00000000001 не стало вдруг нулем. Сейчас я всегда использую тип float и вижу что часто присваивая переменно например 1 на деле могу получить 1.999999856752. В подавляющем большинстве случаев это приемлемая точность, но тем не менее было бы здорово, если бы вычисления стали еще точнее. Поможет ли мне в этом double или этот тип всего лишь увеличивает емкость, позволяя использовать бОльшие числа?
|
|||
|
||||
boostcoder |
|
|||
![]() pattern`щик ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 5458 Регистрация: 1.4.2010 Репутация: 49 Всего: 110 |
нет. создаст еще больше проблем. для подобных задач существует прекрасная и очень сильно проверенная библиотека GMP. ну или на худой конец, переходи на использование std::complex. |
|||
|
||||
Фантом |
|
|||
![]() Вы это прекратите! ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1516 Регистрация: 23.3.2008 Репутация: нет Всего: 49 |
Если механически везде поменять float на double, то где-то поможет, а где-то, наоборот, ухудшит ситуацию. При грамотном использовании - поможет. Но лучше давайте сразу отделим мух от котлет. Вам нужна относительная погрешность вычислений 10^{-10} или абсолютная погрешность 10^{-10} (с сравнительно большей относительной)? В первом случае без double не обойтись, во втором - float вполне может и хватить. |
|||
|
||||
kosmonaFFFt |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 538 Регистрация: 14.4.2008 Где: Иннополис Репутация: нет Всего: 5 |
А чем поможет std::complex? Он ведь тоже базируется на double, float и т. д. -------------------- ![]() |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
Лучше с самого начала везде использовать double - кроме массивов и других случаев, когда нужно экономить память.
Все функции из math.h работают с double - при их вызове float конвертируется в double, а результат наоборот. Регистры математического сопроцессора имеют тип long double, команды сохранения из его регистра в float и загрузки из float более медленные. Так что с double будет работать не только точнее, но и чуть быстрее. А если точности double не хватает - нужна специализированная библиотека. |
|||
|
||||
Alexeis |
|
|||
![]() Амеба ![]() Профиль Группа: Админ Сообщений: 11743 Регистрация: 12.10.2005 Где: Зеленоград Репутация: 12 Всего: 459 |
Не всегда поможет. Числа float и double имеют фиксированное число цифр. Например если к числу 9 000 000 прибавить 0. 000 009, то мы потеряем младшую часть, она будет попросту отброшена хоть то float, хоть то double. Бывает, что при вычислениях такие прибавления или умножения делаются в качестве промежуточных вычислений, а конечный результат малый. В этом случае можно получить погрешность в 1000% и больше. Кроме того на точность вычислений еще влияют настройки сопроцессора. Например при подключении DirectX автоматически снижается точность всех флоат вычислений и даже дабл будут считаться с точностью флоатов. Также потери точности легко добиться при возведении чисел в степень. Возвели в квадрат или куб, че нить прибавили, извлекли корень и младшие разряды пошли по бороде.
Добавлено через 4 минуты и 58 секунд
Если говорить в общем случае, то это неверно. Лично я работал с аппаратурой где флоат считался раз в 50 быстрее дабла, не говоря уже об операциях типа копирования массивов или о объеме занимаемой памяти. Кроме того операции с флоатами могут оптимизироваться на SSE и в результате получаем скорости много выше. -------------------- Vit вечная память. Обсуждение действий администрации форума производятся только в этом форуме гениальность идеи состоит в том, что ее невозможно придумать |
|||
|
||||
Нитонисе |
|
||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
А есть где-то обзор данной библиотеки на русском? И есть ли смысл полностью заменить float на данный тип? Речь идет о скорости вычислений и занимаемой памяти. Может быть уместно разделять задачи, где нужна повышенная точность и использовать там GMP и менее важные вычисления, где и float сойдет?
Мне нужна точность хотябы в 10-ом знаке после запятой, чего float не позволяет. Порой заведомо зная что в некоторой части вычислений будут малые числа я специально их увеличиваю в 100 000 раз, чтобы программа не сочла где-то что значением малого числа можно пренебречь и приравнять его к нулю, в результате чего бывает ошибка деления на ноль. Попутный вопрос. Имеет ли смысл увеличивать числа типа float с целью увеличения точности? Вот если записать float a=1, то можно получить a=0.99999957482. А если записать float a=100000, ведь не получится a=99999.957482, скорее что-то типа а=99999.99999957482. Это сообщение отредактировал(а) Нитонисе - 27.8.2012, 10:22 |
||||
|
|||||
Alexeis |
|
|||
![]() Амеба ![]() Профиль Группа: Админ Сообщений: 11743 Регистрация: 12.10.2005 Где: Зеленоград Репутация: 12 Всего: 459 |
- Нет. числа float отдельно хранят значащие цифры, отдельно степень. Просто увеличиться значение степени. float хранит 7-8 десятичных цифр. double что-то около 15-16. Чтобы сказать точнее нужно смотреть спецификацию. По степени запас флоата 10^38й степени. Точность в 10м знаке обеспечит только double (long double) и то если не будет операций умножения на большие числа или возведения в степень. Добавлено через 4 минуты и 41 секунду Можно кстати википедию почитать http://ru.wikipedia.org/wiki/%D0%A7%D0%B8%...%82%D0%BE%D0%B9 Более подробно описано. Это сообщение отредактировал(а) Alexeis - 27.8.2012, 10:37 -------------------- Vit вечная память. Обсуждение действий администрации форума производятся только в этом форуме гениальность идеи состоит в том, что ее невозможно придумать |
|||
|
||||
Фантом |
|
|||
![]() Вы это прекратите! ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1516 Регистрация: 23.3.2008 Репутация: нет Всего: 49 |
Вот меня и интересует, что Вы под этим понимаете. Вообще говоря, float позволяет работать с числами до 10^{-38}. только вот относительная погрешность будет плохой. Нет, это бессмысленно. Иногда можно добиться некоторого улучшения точности, добившись того, чтобы все числа были близки к 1, но вот обратный процесс ничего полезного не даст. P.S. Кстати, полезно иметь в виду, что 99% проблем такого рода решаются не выбором типов, библиотек и т.п., а разумным преобразованием алгоритма вычислений. |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
||||
|
||||
Alexeis |
|
||||
![]() Амеба ![]() Профиль Группа: Админ Сообщений: 11743 Регистрация: 12.10.2005 Где: Зеленоград Репутация: 12 Всего: 459 |
Относительная погрешность постоянная. Она вычисляется как отношение погрешности к самой величине. Величина увеличилась, ну так и погрешность увеличилась пропорционально, а отношение сохранилось. Добавлено @ 11:54
Так и будет. Числа с плавающей точкой как раз для этого. Вопрос лишь сколько значащих цифр нужно сохранять. -------------------- Vit вечная память. Обсуждение действий администрации форума производятся только в этом форуме гениальность идеи состоит в том, что ее невозможно придумать |
||||
|
|||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
||||
|
||||
Фантом |
|
|||
![]() Вы это прекратите! ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1516 Регистрация: 23.3.2008 Репутация: нет Всего: 49 |
float для этого более чем достаточно. Ну да, правильно. А что, собственно, Вы планировали получить? Операнды имеют относительную погрешность около 10^{-6}, при перемножении относительные погрешности складываются, соответственно, относительная погрешность результата имеет тот же порядок. Добавлено через 2 минуты и 5 секунд Не совсем. На границах возможных значений типа относительная погрешность в любом случае будет расти. |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
А планировал получить 1 Е-20 ![]() |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
В float под мантиссу выделяется 23 бита, это 6 знаков с небольшим запасом в десятичной системе. При умножении относительная погрешность удваивается. Вот и получаете 6 знаков точности. У long double - мантисса 64 бита, примерно 18 знаков точность. больше без спец. библиотеки не получите. Денежные суммы можно хранить в копейках вместо рублей, чтобы не было ошибок округления, В других случаях умножение на коэффициент обычно не поможет. Если решаешь уравнение: X^2 + 2000*X + 1 = 0; X1 = 1000 + sqrt(999999); X2 = 1000 - sqrt(999999); X2 лучше вычислять по формуле X2 = 1 / X1; |
|||
|
||||
Фантом |
|
|||
![]() Вы это прекратите! ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1516 Регистрация: 23.3.2008 Репутация: нет Всего: 49 |
Так Вы его и получили. Я совершенно серьезно: при имеющейся точности операндов этот ответ равен 1E-20. Ничего подобного. Вы выполняете вычисления с некоторой точностью, поэтому на каждой стадии неизбежно получаете какую-то погрешность, полное устранение которой невозможно. Соответственно, Вы задали себе принципиально нерешаемую задачу (да еще и бессмысленную с практической точки зрения). Так что дело действительно не в типах и библиотеках. Вам надо просто честно получить оценку погрешности вычисляемых результатов, затем - такую же оценку для "нуля" в контрольном критерии, а затем сравнивать получающуюся невязку с нулем с учетом этой оценки. Если разность меньше - все нормально. |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
А как оценить погрешность вычисления? Я-то сейчас просто грубо отсекаю числа меньше 0.001, как равняющиеся нулю. Но иногда такой подход нежелателен, я отсекаю не нули, а просто малые числа. А так бы действительно - сравнивал бы с погрешностью и жилось бы немного лучше ![]() |
|||
|
||||
Alexeis |
|
|||
![]() Амеба ![]() Профиль Группа: Админ Сообщений: 11743 Регистрация: 12.10.2005 Где: Зеленоград Репутация: 12 Всего: 459 |
Не все десятичные дробные представимы в двоичном виде не периодичной дробью + погрешность вычислений. Добавлено через 5 минут и 23 секунды
Числа с плавающей точкой не сравнивают с нулем, если с ними производились математические операции. Правильнее проверить, что модуль разницы чисел меньше заранее заданной погрешности. Если числа получаются разного порядка, то можно вычислять относительную погрешность. разницу делить на само число и проверять, что погрешность заданное число процентов. -------------------- Vit вечная память. Обсуждение действий администрации форума производятся только в этом форуме гениальность идеи состоит в том, что ее невозможно придумать |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
При умножении, делении - относительные погрешности складываются.
При сложении и вычитании - складываются абсолютные погрешности. Т.о. при сложении двух чисел одного знака относительная погрешность сохраняется. Вычитания чисел одного знака следует избегать, по возможности использовать альтернативные формулы для вычислений. |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Я так и поступаю. Просто я погрешность задаю жестко 0.001. Наверно это не лучший способ. А можно пример? А то несовсем понятно.
Какова точность вычисления с? |
|||
|
||||
Alexeis |
|
|||
![]() Амеба ![]() Профиль Группа: Админ Сообщений: 11743 Регистрация: 12.10.2005 Где: Зеленоград Репутация: 12 Всего: 459 |
Нитонисе, суммарная абсолютная погрешность вычисляется через сумму частных производных.
Если da = db = eps - машинное эпсилон Тогда dc = (|b + 1| + |a|) * eps -------------------- Vit вечная память. Обсуждение действий администрации форума производятся только в этом форуме гениальность идеи состоит в том, что ее невозможно придумать |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
||||
|
||||
Фантом |
|
|||
![]() Вы это прекратите! ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1516 Регистрация: 23.3.2008 Репутация: нет Всего: 49 |
Хм... вообще говоря, в двух словах не расскажешь. Есть древняя, но хорошая книга Демидовича и Марона "Основы вычислительной математики", ее легко найти в сети. Скачайте и прочитайте первую главу, это при таких занятиях будет очень полезно. |
|||
|
||||
Alexeis |
|
|||
![]() Амеба ![]() Профиль Группа: Админ Сообщений: 11743 Регистрация: 12.10.2005 Где: Зеленоград Репутация: 12 Всего: 459 |
-------------------- Vit вечная память. Обсуждение действий администрации форума производятся только в этом форуме гениальность идеи состоит в том, что ее невозможно придумать |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Немного изменю вычисление.
Погрешность вычисления получается dc = (a + b)*eps, где eps = 1E-5. Точный результат сложения с = 3.5802468. Погрешность dc = 0.000035802468. 'Это значит, что фактически я могу получить результат сложения с = c +/- dc, так? Как же ж мне вычислить погрешность, когда у меня таких вчислений тысячи? ![]() Я посмотрел самое начало - как я понял книга предназначена для приближенных ручных вычислений. Думаю приближенный ручной счет и приближенный машинный - не стоит сравнивать. |
|||
|
||||
Alexeis |
|
||||
![]() Амеба ![]() Профиль Группа: Админ Сообщений: 11743 Регистрация: 12.10.2005 Где: Зеленоград Репутация: 12 Всего: 459 |
Неправильно. в данном случае максимальная погрешность будет 2 * eps . Производная функции y = a + b по а = 1, по b тоже равна 1, поэтому (1 + 1) * eps = 2 * eps .
Обычно стараются выбрать числа такой точности, чтобы погрешность вычисления была на порядок ниже чем погрешность физических величин. В этом случае ей можно пренебречь. Если используются быстрорастущие функции типа экспонент или обратных тригонометрических, то на некоторых участках функции погрешность может уходить в бесконечность. Перед тем как забивать выражения в компьютер нужно оценивать порядки величин и величины погрешности, которые могут возникать на каждом участке выражения. Если выражение вызывает большую потерю точности, то можно изменить порядок вычислений путем расстановки скобок или всяких ухищрений типа логарифмирования (превращает большие числа в малые). Приведу пример. У меня вычислялась экстраполяция параметра по времени. Для отсчета использовался виндовый высокоточный счетчик. Экстраполяция была квадратичной, поэтому аргумент возведенный в квадрат давал число, которое не влезало в дабл, соответственно младшие разряды красиво отрезались и после операций сложения у меня погрешность улетала в бесконечность. Ситуацию удалось исправить использовав в качестве аргумента не само значение счетчика, а разность между текущим значением и значением в опорной точке. Сразу же значение аргумента уменьшилось на 3-4 порядка, а возведенное в квадрат число стало меньше на 6-8 порядков. -------------------- Vit вечная память. Обсуждение действий администрации форума производятся только в этом форуме гениальность идеи состоит в том, что ее невозможно придумать |
||||
|
|||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Дело в том, что это сложно. По одному и тому же алгоритму могут быть обсчитаны как очень крупные, так и очень малые числа... |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
Можете оценивать точность так:
Это сообщение отредактировал(а) math64 - 27.8.2012, 16:02 |
|||
|
||||
Alexeis |
|
|||
![]() Амеба ![]() Профиль Группа: Админ Сообщений: 11743 Регистрация: 12.10.2005 Где: Зеленоград Репутация: 12 Всего: 459 |
Не сомневаюсь. Вероятно, следует использовать использовать различные тесты для проверки таких алгоритмов и делать шаг проверки аргументов экспоненциальным, чтобы охватить проверкой широкий спектр аргументов. Простого способа "сделать все хорошо" я не знаю ![]() -------------------- Vit вечная память. Обсуждение действий администрации форума производятся только в этом форуме гениальность идеи состоит в том, что ее невозможно придумать |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
||||
|
||||
Фантом |
|
||||
![]() Вы это прекратите! ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1516 Регистрация: 23.3.2008 Репутация: нет Всего: 49 |
Если аналитическими методами не воспользоваться - да. В данном случае разницы никакой. У машинных вычислений есть дополнительные особенности, но сначала все равно стоит разобраться с общей базой. И... не сочтите за наезд, но, судя по задаваемым вопросам, Вы с вычислительной математикой совершенно не знакомы, так что отдельными комментариями на форумах тут не отделаться.
А Вы уверены, что Вашу задачу нельзя предварительно обезразмерить? Ситуации, когда параметры вычислительного алгоритма действительно необходимо менять на несколько порядков, в реальной жизни встречаются очень редко. |
||||
|
|||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
Числа должны задаваться с погрешностью на порядок больше чем возможности представления числа в компьютере (иначе нужно заботиться о правильном округлении результата - сумму минимумов в меньшую сторону, сумму максимумов - в большую и т.д.) Это сообщение отредактировал(а) math64 - 27.8.2012, 21:27 |
|||
|
||||
mrgloom |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 829 Регистрация: 8.6.2011 Репутация: нет Всего: нет |
а вообще есть какая нибудь книжка про числа с плавающим знаком приминительно к численным методам?
|
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
||||
|
||||
Randajad |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 295 Регистрация: 15.3.2012 Репутация: 8 Всего: 8 |
А есть еще __float128...
|
|||
|
||||
bsa |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 63 Всего: 196 |
Все очень просто. В компьютерах числа с плавающей точкой представлены в виде структуры, состоящей из 4-х полей: знак мантиссы (+ или -), мантисса (собственно само число), знак экспоненты и сама экспонента (степень двойки). Легче всего это понять тем, кто хорошо знает физику. Там всегда числа представлены в виде: +1,23456 * 10^-10. Тут тоже самое (+0100101 * 2 ^ -010111011). В отличие от физики у машин, обычно, все числа имеют ограничения по количеству знаков (т.е. цифр). Например, число одинарной точности может иметь 23 разряда мантиссы. Таким образом, это где-то 7-8 десятичных знаков. А теперь представим, что у нас есть число 1,2345678 и мы прибавляем к нему 0,00000001. Ожидается результат 1,23456781; но будет, например, 1,2345678. Это и есть одна из основных проблем. Именно поэтому числа с плавающей точкой нельзя использовать для финансовых расчетов. Другая проблема заключается в том, что числа могут быть почти равными, но не быть равными. При выводе на экран они равны, но операция сравнения возвращает false. Именно поэтому необходимо всегда сравнивать модуль разницы таких чисел с некоторым "малым числом" (эпсилон), которое выбирается согласно поставленной задаче, так как самая малая разница чисел с экспонентой +30 будет отличаться от самой большой для чисел с экспонентой -20. Детально: Число одинарной точности |
|||
|
||||
mrgloom |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 829 Регистрация: 8.6.2011 Репутация: нет Всего: нет |
http://redwood.berkeley.edu/w/images/b/bd/20080815pi.pdf
наткнулся на статью по CUDA и Floating Point. например страница 15-16 не знал что так бывает и что вообще надо над этим задумываться. |
|||
|
||||
FCM |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 461 Регистрация: 30.3.2009 Репутация: нет Всего: 9 |
http://www.softelectro.ru/ieee754.html Кроме того, полезным будет посмотреть начало Вержбицкий "Основы численных методов" 2-ое изд-е М.: "Высшая школа" 2005. |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
FCM, немного не то. По вашей ссылке описано как хранит числа с плавающей запятой компьютер. Меня же больше интересует как программировать с учетом способа записи таких чисел компьютером. Некоторые примеры в описании стандарта автор привел, но это так - для общего сведения. Хотелось бы более почитать более объемную и системную работу в этом направлении.
|
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
Нитонисе, если не волнует скорость вычислений, может восползоваться классом Approx (дописав нехватающие методы)
Принципы такие: - каждое приближённое число хранится в виде пары (min, max) - при каждом действии (сложение, вычитание, умножение, деление, ...) вычисляются новые (min, max). - с учётом того, как хранятся данные в компьютере, min округляется в меньшую сторону, max в большую. - при делении на число, приблизительно равное 0, результат (-DBL_MAX, DBL_MAX) - если есть две формулы для вычислений (как я показывал на примере квадратного уравнения) можно почитать по обеим и объединить результаты. |
|||
|
||||
FCM |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 461 Регистрация: 30.3.2009 Репутация: нет Всего: 9 |
Вержбицкого все же посмотри и указанную там библиографию. А вообще - "Двойная точность - вежливость программистов" Саттер. |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
А насколько перспективна идея представлять все числа в виде дробей?
|
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
Если у тебя только операции + - * / можешь работать с дробями. Но вероятно, точности int и даже int64 не хватит
a/b + c/d = (a*d + b*c)/(b*d) если дробь несократимая, может получиться переполнение в знаметателе. exp особо не поможет - будут округления и нужно будет все равно оценивать ошибку вычислений. Ещё можно хранить числа в виде (мат. ожидание, среднее квадратическое отклонение), полагая что распределение близкое к нормальному. |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
максимальное значение int - 2 147 483 647. Любую последовательность чисел можно представить 9-ю разрядами, если реальное число требует больше, то можно выделить еще один int для второй части числа из 9-ти разрядов. То есть например число 999 999 999 888 888 888 будет храниться в двух переменных типа int, в одной будет 999 999 999, во второй 888 888 888, только надо придумать как оперировать такими числами.
|
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
Как хранить число в двух int думать не надо, для этого есть __int64 (long long в gcc).
Для более длинных целых чисел можно найти в инете соответствующую библиотеку. С помощью такой библиотеки можно посчитать Пи с тысячью знаков после запятой. Только при хранении чисел в виде дробей это чаще всего будет не оправдано. |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
||||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Если я буду иметь возможность хранить числа в дробях, где числитель и знаменатель могут быть представлены числами из 300 (что соответсвует максимальной емкости типа double) или дальше больше знаков, неужели этого не хватит для практических вычислений? Конечно, в определенный момент может наступить переполнение, но ведь оно может наступить и при работе с double. При этом работая с double точность теряется, чем больше вычислений мы производим, а при работе с дробями не теряется вообще.
|
|||
|
||||
Alexeis |
|
|||
![]() Амеба ![]() Профиль Группа: Админ Сообщений: 11743 Регистрация: 12.10.2005 Где: Зеленоград Репутация: 12 Всего: 459 |
Какие еще 300 ? У дабла 15-16 цифр. Если нужно максимальное число цифр, то тут я думаю поможет __int64 . У него 19 значащих цифр. По размеру он как double, только не хранит в себе показатель степени и вообще сам по себе число целое. -------------------- Vit вечная память. Обсуждение действий администрации форума производятся только в этом форуме гениальность идеи состоит в том, что ее невозможно придумать |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Я не про точность, а про емкость. Тип double может иметь максимальное значение состоящее из порядка трехсот знаков. При этом точность будет, как вы говорите, в 15-16-ти. А что если int будет иметь возможность хранить такие же большие числа? Он все равно не будет годиться для дробного представления чисел? Я тут набросал класс super_int, который может хранить числа примерно такой же размерности как и double. Вот если ими представить дробь, неужели в практических вычислениях все равно будет переполняться этот тип?
Основная идея - в каждом элементе массива value хранятся данные по трем разрядам. То есть число 123456 будет представлено как value[0] = 456, value[1] = 123, rang = 2. Таким образом можно хранить числа примерно 1E+300. Из математических операторов реализовал только оператор сложения super_int. Каковы перспективы представления дробей такими типами данных? То есть в числителе и знаменателе будут super_int. |
|||
|
||||
FCM |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 461 Регистрация: 30.3.2009 Репутация: нет Всего: 9 |
||||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
Зачем было предложено хранить числа в виде дробей? чтобы избежать ошибок округления. Как только будет не хватать значащих цифр - будут ошибки округления, и цель не будет достигнута.
Кроме того, дроби помогут только при арифметических действиях, а ты упоминал про синус ... Вот как должно делаться некоторые операции с неограниченной разрядностью:
|
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Так в том-то и штука, что если есть возможность представить числитель и знаменатель неограниченным количеством знаков (над реализацией надо подумать), то цель будет достигнута. Ведь все операции выполняются с целочисленными типами. По поводу тригонометрии и прочих математических операций - надо подумать. Главное здесь - это принципиальная возможность достижения условно неограниченной точности вычислений с использованием чисел, представленных в виде дроби с условно бесконечным числителем и знаменателем. Ну например - неужели не хватит для практических вычислений использование дробей, где числитель и знаменатель могут состоять из миллиона значащих цифр? |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
Ну попробуй, используя только обычный int, без потери точности посчитать сумму первых членов ряда в виде дроби: 1 + 1/2 + 1/3 + 1/5 + 1/7 + 1/11 + 1/13 + ... + 1/p До какого p хватит точности? Сложение с неограниченным числом знаком, умножение и деление на обычное число, преобразование к десятичному виду я тебе написал (для чисел без знаков). Возможно, в коде есть ошибки - писал прямо на экране. Например, нет оператора копирования. Вычитание делается аналогично сложению. Умножение в столбик написать тоже очень просто, быстрое умножение - сложнее. Нужно добавить код для убирания незначащих нулей. Преобразовывать в десятичный вид и обратно можно сразу по 9 цифр - деля и умножая на 1000000000U. Дробь можно хранить в виде структуры (знак, числитель, знаменатель); |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
А зачем использовать обычный int? Надо использовать super_int с неограниченным количеством значащих цифр (ну, конечно, условно неограниченным). С вашим классом я, к сожалению, не разберусь. Либо у меня знаний языка не хватает, либо у вас опечатки. Наример член класса data указывает на неизвестный мне тип unsigned. |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
unsigned - это ключевое слово языка C++. эквивалентно unsigned int - тот же int, но без знака.
Если вам нужно для дробей, знак проще учитывать отдельно, чем реализовавать знаковую арифметику. data указывает на массив беззнаковых unsigned int длиной length. В полной реализации нужно две length: сколько выделено и сколько используется. data[0] - младшие 32 бита, data[1] - следующие 32 бита и т.д. Реализуется сложенене, вычитание, умножение, деление "в столбик", c "цифрой" unsigned int (беззнакое 32-разрядное целое в диапазоне 0..0xFFFFFFFFu). При операциях с такими "цифрами" используется typedef unsigned __int64 uint64 - беззнакое 64-разрядное целое в диапазоне 0..0xFFFFFFFFFFFFFFFFuLL - две 32-разрядные "цифры" - чтобы не было переполнение. старшая "цифра" получается сдвигом на 32 бита. |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Вот ваш оператор сложения. Прокомментируйте вопросы, которые я закомментировал.
|
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
|
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
math64, попробовал реализовать ваш класс. Исправил опечатки и мелкие неточности. Не разобрался с вашей функцией toString, мне проще применить для индекации возвращаемое значение типа UnicodeString. Но и без этой функции возникли ошибки утечки памяти. Что-то надо дорабатывать. И еще я так понял надо в класс добавить переменную, хранящую знак числа.
И вообще, насколько я понимаю суть вашего класса в том, что это "супер число" равно сумме чисел массива data? Это ж сколько может понадобиться памяти, чтобы представить число с миллионом значащих цифр?... Мне кажется это не лучший вариант. Я тут переделал свой класс super_int. Число хранится в std::vector<short int> value. Каждый элемент вектора не более 9999. Например число 111122223333 будет записано в таком векторе так value[0] = 3333, value[1] = 2222, value[2] = 1111. Мне кажется перспектив у такой конструкции больше с точки зрения хранения больших чисел с меньшими затратами памяти. На малых числах этот тип скорее будет проигрывать. Ведь если таким образом хранить число 1 уходит 32 байта. Правда я не урезаю вектор под фактическую заполненность. Он ведь вроде резервирует пустые ячейки для записи. Это сообщение отредактировал(а) Нитонисе - 31.8.2012, 17:01 |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
std::vector<> хранит внутри себя массив + небольшую кучку информации - простой массив, однозначно, требует меньше места.
Ты хранишь в каждом short число от 0 до 9999, когда unsigned short может хранить числа от 0 до 65535 - у тебя однозначно, уйдёт больше памяти. У меня числа хранятся в двоичном виде - компактнее не придумаешь (с условием без потери точности). Но сложнее преобразовывать в десятичную систему. Я использую unsigned (32 разряда) и unsigned __int64 (64 разряда) вместо unsigned short (16 разрядов) и unsigned - это не даёт экономии памяти, но ускоряет вычисления. (32 разряда - не менее 9 десятичных цифр, против твоих 8 цифр в двух short) Утечка памяти - я упоминал, что нужен оператор присваивания. без него будет утечка. По уму, нужно реализовывать так:
Проверил - код работает. Учти: я пишу объявление методов класса и реализацию вместе для краткости; тебе их нужно будет разнести на .h и .cpp Если пользуешься Visual Studio - можешь рассмотреть возможность перехода на C#. Там есть BigInteger готовом виде. BigInteger есть также а Java - с исходниками (алгоритмы можно стянуть оттуда). Наверняка есть готовая реализация и на C++ - но придётся искать в инете. Это сообщение отредактировал(а) math64 - 1.9.2012, 20:30 |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Я пишу в C++ Builder XE и разделять код на заголовочный файл и файл реализации необязательно, скинул все в файл h и отредактировал (у вас было много опечаток). Проект скомпилирвал, но при попытке сложить два простых числа выдал ошибку, подсветив строку деструктора, где освобождается память в data. Я не стал дальше разбираться. Конечно ваш метод привлекает тем, что память используется максимально компактно и арифметические действия вроде просто реализуются (у меня сложнее). Возможно и скорость вычислений будет выше (хотелось бы кстати сравнить). Но мне с вашим кодом сложно разобраться ![]() По поводу памяти выделяемой для моего типа данных. Использую std::vector<short int>. Выделяемая память - sizeof(super_int) = 32 байта. Добавил переменную short int для хранения знака. Размер почему-то стал 40 байт, хотя по идее должен был стать 34. Судя по размеру вектора - при инициализации он зарезервировал 32/2=16 ячеек. А это значит, что максимальное число, котороя я могу в него помсетить без дополнительного выделения памяти - 9999 E+60. Какое максимальное число вы можете разместить в своем SuperInt в таком же объеме памяти (32 байта)? Это сообщение отредактировал(а) Нитонисе - 31.8.2012, 21:50 |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
9999E+60 - 64 цифры. У Вас 16 short, 32 байт (полезные данные) в куче, плюс накладные расходы sizeof(super_int) - как я понял, ещё 32 байт, за счёт использования vector<>. Итого 64 байта, если без выравниваний.
У меня будет 214 бит - 7 unsigned, 28 байт (полезные данные). Накладные расходы: usecnt, alloc, length, p - 16 байт, из них 12 перед 28 байтами в куче (40 байт) + 4 байта на стеке. Второе число с тем же значением стоит 4 байта, вне зависимости от значения. Весь класс в одном заголовке - неплохо для тестирования. В рабочей программе, когда заголовок будет инклюдиться много раз (а полная версия класса будет длиннее тестовой), это существенное замедление работы. В предудущем сообщении исправил код на рабочий, добавил умножение и деление на обычное число и тестовый main(); Это сообщение отредактировал(а) math64 - 31.8.2012, 23:17 |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Нет. У меня член класса вектор (пустой) занимает 32 байта. Этот размер я замерил функцией sizeof(). Поскольку там тип short int (2 байта), значит зарезервировано 16 ячеек. При заполнении 17-ой будет выделяться новая память, насколько я понимаю. Так вот до этого самого выделения новой памяти я в 32 байтах могу разместить огромадное число 9999E+60. У вас же, насколько я понял, истинное значение числа равняется сумме чисел из массива data. Один элемент массива (unsigned int) занимает 4 байта. В 32 байтах поместится 8 таких чисел. Каждое число максимально равно 4.3E+9, умножив на 8 получим максимальное значение вашего типа на 32 байтах - 34.4Е+9. То есть намного меньше чем у меня. Это не учитывая обслуживающие дополнительные члены (для хранения знака или длины массива). |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Все таки идея с представлением чисел дробями имеет слабые перспективы, так как я что-то не представляю как такие числа возводить в дробную степень (звлечение квадратных, кубических корней).
|
|||
|
||||
mes |
|
|||
любитель ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 7954 Регистрация: 14.1.2006 Репутация: 144 Всего: 250 |
такое представление чисел имеет свое название (не могу сходу вспомнить), есть математика для них и библиотечки попадались.. это я к тому что все это уже придумано и реализовано, гугль должен помочь.. |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Да, но корень квадратный, например, из 3 не извлекается. Стало быть надо хранить корень (а в общем случае - дробную степень) просто как характеристику числа и использовать каким-то образом ее в вычислениях. Вырисовывается сложная задача. Это сообщение отредактировал(а) Нитонисе - 1.9.2012, 10:54 |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
Извлечение квадратного корня:
Добавлено через 6 минут и 42 секунды А я сразу про подобное предупреждал. Большинство чисел - иррациональны и даже трасцендентны, и в копмьютере представляются только приближенно. Хотя некоторые, например, Пи, можно посчитать с любой заданной точностью. |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
ну то что его приближенно можно вычислить - это понятно. Но ведь вся идея была в том, чтобы вычисления были точными, поэтому приближенные методы не годятся. В начале темы говорили о библиотеке GMP, видимо это лучший способ реализовать высокую точность в расчетах? Хотел бы вернуться к названию темы. Где-то в первых постах говорили, что double не может повысить точность по сравнению с float. Но почему? Ведь double имеет точность 15 знаков, а float 7. То есть по идее от операций с double следует и точность бОльшую ожидать. Попутно возник вопрос. Если я где-то в вычислениях использовал разнотипные данные (например double умножаю или складываю с float), как это отразится на точности? При выполнении операции данные ведь преобразуются к одному типу. Вопрос - к какому? |
|||
|
||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
При умножении double на float, предварительно float преобразуется в double. Если float представлял число точно (например, 2.f) - результат будет с точностью double. Если нет - с точностью float. Использование double НЕ УМЕНЬШИТ точность вычислений - но в некоторых случаях не повысит. Тоже вычисление корня квадратного уравнения, близкого к 0 - использование double с традиционной формулой даст худший результат, чем вычисление другого корня в float и использование теоремы Виета. |
|||
|
||||
Нитонисе |
|
||||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Допустим мне надо произвести высиления с точностью double. Как их правильно записать:
То есть хуже не будет, а скорее всего будет лучше, но не всегда? ![]() Но вот вы сразу опровергли предыдущее свое утверждение, показав, что в некоторых случаях double хуже float. Только не понятно почему? math64, вы предлагали использовать класс Approx для контроля погрешности. Я бы его немного преобразовал.
При всех математических операциях dv будет меняться. После длительных вычислений я получу некое dv с которым будет более менее удобно работать, выполняя логические действия. Например если v < dv - значит число равно нулю. Сравнивая числа, если fabs(n1.v-n2.v) < max(n1.dv,n2.dv), то числа равны. Только вот не совсем понимаю как мне этот параметр dv вычислять. Подскажите чему он будет равен при стандартных математических операциях (+, -, *, /), а также при возведении числа в дробную степень и применении тригонометрических функций? Это сообщение отредактировал(а) Нитонисе - 2.9.2012, 11:14 |
||||||
|
|||||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Да и еще подумал - что тольку от этого параметра dv, если он и сам вычисляется не точно, а с погрешностью...
|
|||
|
||||
FCM |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 461 Регистрация: 30.3.2009 Репутация: нет Всего: 9 |
Переход к double решает многие проблемы. Можешь проверить, написав свою программу через typedef-псевдоним, в качестве которого выбирать либо float, либо double.
(Есть еще 80-битные и 128-битные floating point, но без веских оснований не стоит заморачиваться.) Это сообщение отредактировал(а) FCM - 2.9.2012, 19:02 |
|||
|
||||
math64 |
|
||||||||||||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
В результате получишь 0. 1 делится на 3 как целое число, результат = 0. Нужно
или
лучше чем
но хуже
а лучше всего
Ещё пример: Нужно посчитать 1 + 1/2 + 1/3 + ... 1/n; где 1/n - намного меньше "компьютерого эпсилон". Если будешь считать попорядку - начиная с какого-то члена добавление очередного слагаемого не будет изменять сумму, а ведь ряд расходится! А вот если начнёшь складывать с конца - получишь результат с приемлемой точностью даже с double, не прибегая к точному вычислению с помощью дробей из длинных чисел. Для контроля за погрешностью я предлагал два врианта: 1. (минимум, максимумум) - абсолютно точно известно, что число не вылезет за пределы диапазона. Но по мере вычисления диапазон часто быстро расширяется, в то время как чило имеет нормальное распределение с максимумом где-то в середине диапазона. Поэтому второй вариант: 2. (мат. ожидание, среднее квадратичное отклонение) С вероятностью 95% (если правильно помню тер.вер.) число находится диапазоне 3 СКО. Но всё таки всегда будет ненулевая вероятность, что число может оказаться равным 0. Это сообщение отредактировал(а) math64 - 2.9.2012, 20:24 |
||||||||||||||
|
|||||||||||||||
math64 |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
Ну а подсчёт вероятности такой:
X = x + dx*nx; // nx - случайная величина нормального распределения СКО = 1, среднее = 0 Y = y + dy*ny; Считая, что dy и dy достаточно малы, Z = F(X,Y) = F(x,y) + dx*nx*dF/dx + dy*ny*dF/dy + ошибка вычислений. где dF/dx и dF/dy - частные производные и вычисляй СКО для dx*nx*dF/dx + dy*ny*dF/dy + ошибка вычислений, считая что ошибка вычислений - случайная величина с равномерным распределением (-eps*Z,eps*Z) Это сообщение отредактировал(а) math64 - 2.9.2012, 20:47 |
|||
|
||||
Нитонисе |
|
||||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Похоже что так. Единственная для меня проблема здесь в логических операциях с double. Сравниваю так |a-b| < min. То есть если модуль разницы меньше некоего минимального числа, то a = b. Весь вопрос в том что выбрать за минимальное число. До сих пор работая с double я брал число 0.001. Для моих задач чаще всего хватало, но не всегда. Хотелось бы как-то обоснованней назначать его. Вот если бы параллельно основным вычислениям шел бы и расчет погрешности, то эту погрешность я бы и принимал в качестве минимальной разницы чисел. math64,
Это две эквивалентные записи? В первом случае ведь единица приводится к double неявно. Может она в вычислениях к float приведется? И почемы вы тройку не приводите к double? В операции деления она автоматически преобразуется в double, так как единица принадлежит этому типу? А что если не преобразуется? А если к double привести тройку, а единицу нет (1/(double)3) - то тройка преобразуется к int? Правильна ли ниже запись цикла, где надо считать с точностью double, но счетчик цикла типа int.
Так ведь и если с конца начать складывать, то до некоторого числа сумма будет равняться нулю.
|
||||||
|
|||||||
FCM |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 461 Регистрация: 30.3.2009 Репутация: нет Всего: 9 |
Если речь идет о сравнении в численных итеративных задачах - во многих случаях контролируют относительную точность и (на случай нулевого результата) абсолютную. (Все же посмотри первую главу Вержбицкого, насчет различных видов ошибок и методов их оценки) Это сообщение отредактировал(а) FCM - 3.9.2012, 07:54 |
|||
|
||||
math64 |
|
||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
В первом случае 1. - число типа double (1.f - число типа float), во втором - (double)1 - целое, приводимое к типу double. Во втором случае приведение производится на этапе компиляции, так что записи эквивлентны. ((double)1/3) и (1/(double)3) - тоже эквиваленты ((double)1/(double)3), так как приведение одного операнда к double заставляет компилятора приводить к double другой операнд. Деление будет тоже призводиться на этапе компиляции - так, что писать лучше (1./3) чем 0,333333333333 В случае ((double)i/3); приведение типа и деление будут выполняться на этапе выполнения; 3 будет заменена на 3, на этапе компиляции.
Вычисление призводится с плавающей точкой, а не фиксированной. члены ряда не будут равны 0 очень долго, но будучи прибавлены к накопленной сумме (большей 1), будут сначала терять младшие разряды точности, а затем вовсе не изменять сумму. Точнее свего алгоритм суммурования такой: 1. слагаемые записываются в вектор 2. вектор сортируется по убыванию абсолютной величины. 3. если в векторе не меньше 2 чисел, два последних складываются и заменяются на сумму ; переход к пункту 2; Можешь попробовать на тесте (сложение без сортировки float; сложение c сортировкой float; сложение без сортировки double; сложение c сортировкой double) и сравнить результаты. Правда, сортировка будет происходить неоправданно долго.
В том то и дело, что не равноудалены. Поэтому практичнее считать СКО. (1+-0.1) * (2+-0.1) = (0.9 .. 1.1) * (1.9 .. 2.1) = (1.71 .. 2.31) - середина 2.01, а не 2 |
||||
|
|||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Я посмотрел. Сложный материал для восприятия. Занялся бы более тщательным изучением с целью применения на практике, да вот только смущает, что при вычислении погрешности также допускается погрешность. Дело в том, что где-то мне попадалась на глаза фраза, что если тип приведения не указан явно, то компилятор может преобразовать такое число и к double и к float. Так что лучше наверное указывать тип явно, тем более, что преобразование состоится на этапе компиляции, а значит выполнение будет производиться быстрее. |
|||
|
||||
FCM |
|
||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 461 Регистрация: 30.3.2009 Репутация: нет Всего: 9 |
Погрешность погрешности будем величиной более малого порядка, но рефлексировать (или рекурсировать) на эту тему слишком глубоко не стоит. У Вержбицкого коротенько и наглядно обсуждаюся статистический и технический подходы к оценке погрешности совокупности большого числа вычислений.
Разве приведение будет на этапе выполнения? Это сообщение отредактировал(а) FCM - 3.9.2012, 10:39 |
||||
|
|||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
Немного оффтопный вопрос. Я раньше работал с float и не было проблем перевода float в строку и обратно (FloatToStr и StrToFloat). А что с double? Готовых функций нет? Надо самому писать, либо чьей-то самоделкой воспользоваться?
Добавлено через 7 минут и 51 секунду Похоже FloatToStrF работает со значением long double. А строка в double похоже конвертируется функцией ToDouble. |
|||
|
||||
Нитонисе |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 917 Регистрация: 5.11.2009 Репутация: нет Всего: 2 |
||||
|
||||
math64 |
|
||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2505 Регистрация: 12.4.2007 Репутация: 8 Всего: 72 |
Погрешность хранится отдельно от числа, в виде с плавающей точкой - достаточно точно.
После прибавления к основному числу точность, да теряется. Но ведь сравниваются же два числа так:
или если храним квадрат СКО, при нормальном распределении, с допуском 3 СКО:
Главное место где теряется точность - вычисление delta. Нужно также следить, чтобы при вычислении квадрата не было переполнения и антипереполнения. |
||||
|
|||||
![]() ![]() ![]() |
Правила форума "С++:Общие вопросы" | |
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |