Программирование на Си (повышение точности)

Модератор: Модераторы разделов

Аватара пользователя
Hephaestus
Сообщения: 3728
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Программирование на Си

Сообщение Hephaestus »

Здравствуйте.
Пытаюсь освоить Си.
К сожалению, учили нас толькона Паскале и только на продуктах Борланд (б-р-р-р...), поэтому мозг отравлен. Никак не могу выбраться за пределы Паскалевских рамок.
Книги смотрел разные.
Со скрипом идёт K&R.
Стивен Прата. Его хвалили, но достать практически негде.
Сейчас вот Стефан Кочан на вооружении. На ЛОРе проскальзывало, что это наподобие Праты.
Это всё лирика.

Теперь по теме. В некоторых книгах (не во всех) обращают внимание на то, что числа с плавающей точкой не имеют точного машинного представления. Это действительно так.
Но ни в одной книге мне не попадалось никаких рекомендаций на этот случай.

В одной здешней теме я приводил такой пример из книги:

Код: Выделить всё

var a, b: real;
n: byte;
begin
a:=0.2;
b:=0;
for n:=1 to 50 do b:=b+a;
Writeln('a=', a:20:18, '; b= ', b:20:18)
end.


Очевидно, что результирующим значением переменной b должно быть
ax50=10
Однако, результатом работы этого кода является
a=0.200000000000045475;
b=9.999999999927240420
Заметьте, что хотя реальное значение переменной a больше(!), чем задано в коде, результирующее значение переменной b получилось меньше(!), чем ожидалось.

Приведенные здесь цифры - это результат работы кода, скомпилированного в Borland Pascal.
FreePascal даёт другой результат

Код: Выделить всё

a=   0.200000000000000;
b=   10.000000000000000
Как видите, выглядит намного приличней.
Отсюда делаю два вывода:
1. Результат зависит от кучи факторов, в частности, от компилятора.
2. Получение результата с хорошей точностью в принципе возможно.
Я попробовал переписать данный код на Си.
Более-менее точные цифры получились при использовании long double.
Но всё равно b получилось меньше 10.
Я вот думаю, если Pascal позволяет хорошую точность, то и в Си, наверное, можно.

Вопрос: Как при использовании Си получить высокую точность? Такую же как даёт FreePascal для приведенного примера?
Какие есть способы (методы, правила, приёмы) повышения точности для Си вообще и под Linux, в частности?

Прошу опытных сишников поделиться знанием.
Также буду благодарен за любые рекомендации касательно статей, литературы и пр. по данной теме.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: Программирование на Си

Сообщение eddy »

fflatx писал(а):
21.09.2013 14:03
Как при использовании Си получить высокую точность?

Срочно читать матчасть! Для чисел с плавающей точкой все вычисления невозможно сделать лучше, чем FLT_EPSILON (для float) или DBL_EPSILON (для double). Ну, а если вычислений много, то в результате ошибка округления будет еще выше!

Поэтому-то нельзя сравнивать два числа с плавающей точкой на равенство.

А паскаль просто округлял. Это и в сях можно сделать. А выводить нужное количество цифр после запятой, указывая формат в printf.

RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 21235
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Программирование на Си

Сообщение Bizdelnick »

Во-первых, пример ну очень надуманный.
Во-вторых, желаемый ответ можно получить так:

Код: Выделить всё

#include <stdio.h>

int main() {

    int a, b, n;

    a = 2;
    b = 0;
    for (n = 0; n < 50; n++)
        b += a;
    printf("a=%0.18f\n", (double)b/10);
}

Помимо очевидного b = a * 50, разумеется.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: Программирование на Си

Сообщение eddy »

Bizdelnick писал(а):
21.09.2013 14:31
Во-первых, пример ну очень надуманный.

Не надуманный: где-то (возможно, даже у K&R) я подобное видел, на этом примере объясняется, что числа с плавающей точкой имеют плохонькую точность. И 10 раз по 0.1 не даст 1.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 21235
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Программирование на Си

Сообщение Bizdelnick »

eddy писал(а):
21.09.2013 14:48
Не надуманный: где-то (возможно, даже у K&R) я подобное видел, на этом примере объясняется, что числа с плавающей точкой имеют плохонькую точность.

Вот и именно, что это просто пример того, как не надо делать.
И не говорите, что использовать цикл вместо умножения - не надуманно.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Аватара пользователя
Hephaestus
Сообщения: 3728
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Программирование на Си

Сообщение Hephaestus »

eddy писал(а):
21.09.2013 14:14
А паскаль просто округлял.
Это действительно так?
Если так, то конечно, вопросов никаких нет.
Хотя непонятно, откуда он знает, когда надо округлить, а когда нет.
Но вообще-то это как-то нечестно, по-моему.

Однако на ЛОРе где-то писали, что для Си есть специальная библиотека для вычислений высокой точности.

Речь не идёт о сравнении вещественных чисел на равенство, а просто о выводе и хранении с определенной точностью. Ведь точность, в показанном примере вполне влезает в пределы long double.

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

Код: Выделить всё

float floatingVar=331.79
На экран вывелось с умолчальным форматированием

Код: Выделить всё

floatingVar=331.790009
Ну это уж совсем плохо. Казалось бы, откуда взялась последняя девятка? Ведь переменная задана вполне определенно. Это ведь даже не запредельная точность какая-то.
Это даже не вычисления, а просто вывод на экран. То есть вещественное число хранится чёрт знает с какой точностью.




Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 21235
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: Программирование на Си

Сообщение Bizdelnick »

fflatx писал(а):
21.09.2013 15:14
Речь не идёт о сравнении вещественных чисел на равенство, а просто о выводе и хранении с определенной точностью. Ведь точность, в показанном примере вполне влезает в пределы long double.

Вы накапливаете погрешность в цикле, так что умножайте её на 50. Или используйте умножение вместо цикла.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Программирование на Си

Сообщение drBatty »

fflatx
не путайте пожалуйста точность вычислений с точностью вывода.

Точность вывода в C зависит от printf(3), и регулируется в нужных пределах (можно задать сколько угодно знаков после запятой). То, что вы видите -- это просто умолчание.

Следует заметить, что в общем случае число из одной системы счисления(СС) НЕ переводится в другую СС. К примеру, 1/3 в СС3 есть 0.1, но в десятичной системе, как не крути, всё равно получается меньше (0.33333333333333 меньше 1/3). Т.е. точность перевода в принципе не может быть бесконечной.

Точность вычисления зависит исключительно от железа. Обычно float имеет 32 бита, а double имеет 64 бита. Потому double намного точнее. Однако в конкретной реализации всё может быть и не так. В стандарте C точность чисел НЕ задана. Определено лишь то, что double не хуже float. В конкретных реализациях, например в i686, могут быть и дополнительные типы данных, которые точнее или быстрее double/float.

Следует помнить, что современные CPU(x86_64) умеют за раз обрабатывать вдвое больше float, потому в теории float вдвое быстрее double.

http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Программирование на Си

Сообщение drBatty »

fflatx писал(а):
21.09.2013 15:14
Хотя непонятно, откуда он знает, когда надо округлить, а когда нет.

на выводе округляет. А в сишечке -- не округляет.
fflatx писал(а):
21.09.2013 15:14
Однако на ЛОРе где-то писали, что для Си есть специальная библиотека для вычислений высокой точности.

есть, и не одна. А вот в Lisp'е она по дефолту. Там по умолчанию бесконечная точность для рациональных чисел.
fflatx писал(а):
21.09.2013 15:14
floatingVar=331.790009
Ну это уж совсем плохо.

ну эта точность лучше, чем 1/100000. В числе float ЕМНИП 23 бита в мантиссе, т.е. точность около 1/8388608 доли числа (точнее намного хуже конечно). Ну а так-как число у вас 331, то получается около .0000395, что намного хуже, чем ваше 1/100000. Т.е. радоваться надо -- звёзды сошлись так, что вам несказанно повезло, и точность в 100 раз лучше той, которую гарантировали!

drBatty писал(а):
21.09.2013 17:29
точность в 100 раз лучше той, которую гарантировали!

разгадка проста -- во многих случаях вычисления абсолютно точные, например 1+1 получается 2 в любых СС. Ошибка возникает лишь в случае антипереполнения, да и то лишь тогда, когда корректировочный бит не совсем равен тому, что отбрасывается. Такое в тестовых примерах бывает редко(а на практике постоянно).

Bizdelnick писал(а):
21.09.2013 15:22
Вы накапливаете погрешность в цикле, так что умножайте её на 50.

ну это вы через край хватанули! Умножать её на 50 надо в самом худшем случае, вероятность которого составляет 1:1125899906842624. Т.е. ничтожно мала (хотя и не нулевая). Т.е. вероятность того, что компьютер ТСа загорится синим пламенем намного выше.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Sleeping Daemon
Сообщения: 1450

Re: Программирование на Си

Сообщение Sleeping Daemon »

fflatx писал(а):
21.09.2013 14:03
Здравствуйте.
Пытаюсь освоить Си.
...
Стивен Прата. Его хвалили, но достать практически негде.
...

http://razym.ru/komp/programming/225652-pr...-e-izdanie.html
Спасибо сказали:
Аватара пользователя
Hephaestus
Сообщения: 3728
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Программирование на Си

Сообщение Hephaestus »

Sleeping Daemon , спасибо, конечно, но ссылки на варез запрещены правилами. Советую убрать, пока модераторы нам по башке не надавали.
Кроме того, книга неполная, в ней отсутствует 200 страниц.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: Программирование на Си

Сообщение NickLion »

Bizdelnick писал(а):
21.09.2013 14:57
И не говорите, что использовать цикл вместо умножения - не надуманно.

Это когда элементы одинаковые. Но если они различаются (например, суммирование при численном интегрировании), то умножением не заменить.

fflatx писал(а):
21.09.2013 15:14
Однако на ЛОРе где-то писали, что для Си есть специальная библиотека для вычислений высокой точности.

Например, GMP (GNU MP).
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Программирование на Си

Сообщение drBatty »

fflatx писал(а):
22.09.2013 00:08
спасибо, конечно, но ссылки на варез запрещены правилами. Советую убрать, пока модераторы нам по башке не надавали.

откройте для себя Kademlia. Там всё есть. И да, я книжки покупаю, но ТОЛЬКО после ознакомления с копией из ed2k. Как и фильмы (впрочем, фильмы сейчас настолько поганые, что покупать их глупо ИМХО)
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Программирование на Си

Сообщение drBatty »

NickLion писал(а):
22.09.2013 12:29
Это когда элементы одинаковые. Но если они различаются (например, суммирование при численном интегрировании), то умножением не заменить.

точность не бывает "плохой" и "хорошей". Точность -- жёстко прописанная в стандарте характеристика.

А при численном методе, ответ вы получаете не детерминированный. Т.е. как и при ГА -- может получится, а может и не получится. Смиритесь с этим. Однако, при грамотном кодировании, вероятность быстро получить правильный ответ практически 100%.

Пример: вы вычисляете средний результат 1000 абсолютно точных измерений, однако из-за "кривых" чисел каждое измерение имеет погрешность 2^-8. (это 0.4%). Т.к. погрешности складываются, то результат имеет "точность" ровно 400%! Т.е. если вы получили 68, то _возможно_ что истинное значение 17. Однако, вероятность получить результат 68 в данном случае равна 1/10715086071862673209484250490600018105614048117055336074437503883703510511249361
224931983788156958581275946729175531468251871452856923140435984577574698574803934
567774824230985421074605062371141877954182153046474983581941267398767559165543946
077062914571196477686542167660429831652624386837205668069376

Т.е. практически это ноль.

Практически (с вероятностью >99.999%, можете посчитать точнее), вы получите 17+-0.4%. Именно потому-то float'ы используют в инженерных расчётах. Всё равно вероятность того, что таджик-сборщик что-то спутает, НАМНОГО выше.

Хотя, если расчёты связаны с финансами, то используются целые, а НЕ дробные. (да и то не всегда, вот кредитование вообще считается через задницу, ибо надо математику учить, перед взятием кредита :)
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: Программирование на Си

Сообщение NickLion »

drBatty писал(а):
22.09.2013 13:12
точность не бывает "плохой" и "хорошей". Точность -- жёстко прописанная в стандарте характеристика.

Я где-то это говорил? о_О
Но раз уже затронули — точность бывает недостаточной для данной задачи.

drBatty писал(а):
22.09.2013 13:12
А при численном методе, ответ вы получаете не детерминированный. Т.е. как и при ГА -- может получится, а может и не получится. Смиритесь с этим. Однако, при грамотном кодировании, вероятность быстро получить правильный ответ практически 100%.

Я в курсе. Но, к примеру, если известно, что функция не имеет резких (на уровне размера шага) скачков и интегрирование методом Симпсона даст приемлемую точность. Но вот незадача, точности типа double может не хватить. В таком случае используем GMP и всё "хорошо".
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Программирование на Си

Сообщение drBatty »

NickLion писал(а):
22.09.2013 13:22
Но раз уже затронули — точность бывает недостаточной для данной задачи.

что-то я не припоминаю практических задач, где float'а не хватает...

NickLion писал(а):
22.09.2013 13:22
Я в курсе. Но, к примеру, если известно, что функция не имеет резких (на уровне размера шага) скачков и интегрирование методом Симпсона даст приемлемую точность. Но вот незадача, точности типа double может не хватить. В таком случае используем GMP и всё "хорошо".

это "хорошо" называется быдлокод
ваш случай совершенно эквивалентен случаю, когда для ускорения пузырьковой сортировки предлагают купить новый сервер.

А если голову подключить?

смотрите на картинку:


на первый взгляд непонятно? Ага, это потому-что вы не думаете. А на самом деле, это просто кривая картинка, которая совсем не похожа на RL. IRL расстояние между нулём и точками a, m, b, НАМНОГО БОЛЬШЕ, чем расстояние между самими этими точками. В итоге, эта РАЗНОСТЬ вычисляется с огромной погрешностью, из-за чего метод Симпсона даёт поганый результат, точность и скорость которого даже ХУЖЕ, чем если-бы вы просто взяли нечто среднее между точками.

И что делать? Покупать новый сервер? Не нужно. Достаточно эту полоску считать и хранить в ДВУХ частях. А именно
1. полоска от нуля до точки a (эта точка самая низкая здесь)
2. непонятная фигура ограниченная ординатой a снизу, и нашей параболой сверху.
И вот тогда ваша точность подскочит на много порядков вверх, как и скорость вычисления.


Просто НИКОГДА нельзя складывать сильно разные числа. Метод Симпсона очень хорошо работает если есть участки, где величина производной большая. Т.е. где функция РЕЗКО меняется. В этих местах влияние части №2 на общую сумму велико, и кривая второй степени это учитывает. Но вы её мешаете с остальной полоской, вот и получается у вас fail.

Тут фишка в том, что сама площадь полоски №1 линейно зависит от dx, а вот площадь части №2 -- сильно нелинейно. В итоге полоски №1 схлопываются, а части №2 схлопываются у вас вмести с ними, т.к. вы их замешали. Надо было посчитать ДВЕ суммы, а потом уже делить их на dx. По времени это ровно столько же, но точность на порядки выше.
У вас нет необходимых прав для просмотра вложений в этом сообщении.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: Программирование на Си

Сообщение NickLion »

Ау, при чём здесь всё это? Я о банальном случае, когда в том же float ошибка на каждом этапе 1e-6 (float больше не даст), а в сумме за 10000 шагов это вылазит в 1e-2.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Программирование на Си

Сообщение drBatty »

NickLion писал(а):
22.09.2013 15:36
Ау, при чём здесь всё это? Я о банальном случае, когда в том же float ошибка на каждом этапе 1e-6 (float больше не даст), а в сумме за 10000 шагов это вылазит в 1e-2.

при том, что у меня не вылазит, а у вас -- вылазит. О чём это говорит?
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: Программирование на Си

Сообщение NickLion »

О том, что Вы затачиваете алгоритм под конкретную функцию, чтобы компенсировать ошибку.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Программирование на Си

Сообщение drBatty »

NickLion писал(а):
22.09.2013 15:45
О том, что Вы затачиваете алгоритм под конкретную функцию, чтобы компенсировать ошибку.

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

Код: Выделить всё

(x+x/y)*y != x*y + x

причём это "неравно" очень большое, если y>>x. И мало того, ошибка не только велика, но ещё и не детерминирована, и не подчиняется никаким законам, кроме статистических.

И это общий случай, а вовсе не какой-то специальный. Оно всегда так в численных методах. А ваш GMP -- костыль. На практике быстрее изучить теорию, чем дождаться завершения работы быдлокода с GMP.

(сам по себе GMP конечно нужен. Но для другого)
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
Hephaestus
Сообщения: 3728
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Программирование на Си

Сообщение Hephaestus »

Я даже не ожидал, что тема вызовет такое живое обсуждение.

drBatty писал(а):
22.09.2013 14:39
что-то я не припоминаю практических задач, где float'а не хватает...
Ну например, Вывести на экран число Пи с точностью 7 знаков после запятой.
Может, это и не практическая задача, на практике, в основном бухгалтерия, но тем не менее.

Код: Выделить всё

#include <stdio.h>
int main(void)
{
    float pi;
    pi=3.1415926;
    printf("pi=%9.7f\n",pi);
}
Переменную объявляем как float,
Вывод форматируем, соответственно, тоже для float.
Получаем

Код: Выделить всё

pi=3.1415925
Как видно, есть погрешность в 7 знаке.
Если знаков вывести больше, там будет мусор, поскольку переменная задана с 7 знаками.
Если верить справочнику Шилдта (первое, что нагуглилось), то float имеет диапазон "от 1E-37 до 1E+37, с точностью не менее 6 значащих десятичных цифр", Ну, 6 цифр выводится нормально, а дальше...
(Кстати, у Шилдта годный справочник? А то у него (Шилдта) репутация какая-то странная... Ему даже кличку неприличную прилепили. Подскажите хороший справочник или другую доку, где описаны все эти типы данных, диапазоны и пр., у K&R в приложении приведены диапазоны, а вот про точность - ни слова.)


Вот, собственно, суть темы: Что в приведенном коде неправильно? И как сделать правильно?
Это ведь даже не вычисления, а просто вывод на экран.
Кроме как использовать double я ничего придумать не могу. Там, где не хватит double, будет long double, а там, где не хватит long double... Там я не знаю, как быть.
Я понимаю, что у меня что-то неправильно. Но...
Вы опытный человек, drBatty, вот и расскажите, как правильно.

drBatty писал(а):
22.09.2013 16:03
Например

Код: Выделить всё

(x+x/y)*y != x*y + x

причём это "неравно" очень большое, если y>>x.
А ведь и впрямь не равно.

Код: Выделить всё

#include <stdio.h>

int main(void)
{
        float a,b,c,x,y,pi;
x=10000000;
y=226.5956;

        a=(x+x/y)*y;
        b=(x*y+x);
        c=a-b;
        printf("a=%f, b=%f\n",a,b);
        printf("c=a-b=%f\n",c);

}

Результат

Код: Выделить всё

a=2275955968.000000, b=2275955712.000000
c=a-b=256.000000
Причём, не равно уже в целой части, а не в дробной.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Программирование на Си

Сообщение drBatty »

fflatx писал(а):
22.09.2013 20:36
Ну например, Вывести на экран число Пи с точностью 7 знаков после запятой.

тут float'ов вполне достаточно: 3.1415926. Я это без всяких C помню (:
fflatx писал(а):
22.09.2013 20:36
Может, это и не практическая задача, на практике, в основном бухгалтерия, но тем не менее.

что сразу "бухгалтерия"? Полно и других задач. А в бухгалтерии вам вообще зачем дробные-то? Целых разве не хватает?
fflatx писал(а):
22.09.2013 20:36
Как видно, есть погрешность в 7 знаке.

этот знак на хлеб не намажешь.
fflatx писал(а):
22.09.2013 20:36
Если верить справочнику Шилдта (первое, что нагуглилось), то float имеет диапазон "от 1E-37 до 1E+37, с точностью не менее 6 значащих десятичных цифр", Ну, 6 цифр выводится нормально, а дальше...

fflatx писал(а):
22.09.2013 20:36
(Кстати, у Шилдта годный справочник? А то у него (Шилдта) репутация какая-то странная... Ему даже кличку неприличную прилепили. Подскажите хороший справочник или другую доку, где описаны все эти типы данных, диапазоны и пр., у K&R в приложении приведены диапазоны, а вот про точность - ни слова.)

так..
1. точность и размер чисел в стандарте НЕ ОПРЕДЕЛЁН. Это не баг, а фича. Есть "обычные" стандартные числа для данной платформы "float", а есть числа "с двойной точностью" double (на некоторых платформах их может и не быть, тогда они эквивалентны float. На некоторых платформах ВООБЩЕ НЕТ таких чисел(дробных). На некоторых есть третий (четвёртый и т.д.) типы).
2. многие платформы умеют IEEE 754 float, в частности x86 и x84_64. На таких платформах float 32 бита, и double 64 бита.
3. x86 вообще говоря имеет на борту i8087, который умеет ещё более длинные 80и битные long double, но пользоваться ими настоятельно НЕ рекомендуется, ибо может привести к жутким тормозам на современных CPU. Обычные float/double современный CPU может жрать по много за раз.
4. Шилдт годно пишет, но безнадёжно устарел.
fflatx писал(а):
22.09.2013 20:36
Вот, собственно, суть темы: Что в приведенном коде неправильно? И как сделать правильно?

в коде всё ИМХО правильно.
fflatx писал(а):
22.09.2013 20:36
там, где не хватит long double... Там я не знаю, как быть.

вам же сказали -- подключите GMP. Если вам нравится смотреть на кучу цифр...
fflatx писал(а):
22.09.2013 20:36
А ведь и впрямь не равно.

а людей, которые сравнивают double на равенство, надо УБИВАТЪ!

В мире нет ничего точного, и double -- это просто модель реального мира. Если вы дважды измерите линейкой в 30см расстояние в 30 метров в миллиметрах, у вас получится два разных числа. ОДНО И ТОЖЕ расстояние. Что в этом удивительного?

А ЗАЧЕМ нужны другие числа в компьютере? Из какой-то другой реальности? Не нужны. Ну вот их и нет. Есть неточные.

Да, а сравнивать числа и не нужно, необходимо и достаточно определения факта вхождения некого числа в некий интервал с некоторой заданной вероятностью. При этом, конечно, и число и интервал и вероятность должны быть известны. В виде чисел, а не в виде "хорошее".
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Программирование на Си

Сообщение drBatty »

fflatx писал(а):
22.09.2013 20:36
a=(x+x/y)*y;

кстати это сишечка.
1. компилятор вовсе не обязан считать так, как написано. Он имеет право считать так, как ему хочется. Для него данное выражение тоже самое, что и x*y+x. А может -- нет. Не определено. Читайте про точки следования. Вот если-бы вы написали

Код: Выделить всё

a1 = x+x/y;
a = a1 * y;

то да, пришлось-бы делать так, как вам хочется.

2. данное выражение вообще НЕ ВЫЧИСЛЯЕТСЯ. Вы думаете, что компилятор будет создавать код 2*2? Нет, он просто напишет в коде 4.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
Фантом
Сообщения: 460
ОС: openSUSE

Re: Программирование на Си

Сообщение Фантом »

Не хочу портить столь интересную беседу, но при обсуждении вопросов такого рода все-таки стоит учитывать, что в природе существует целая наука под названием "вычислительная математика". Со своими проблемами, методами и т.д. И если ТС эта проблема волнует всерьез, то надо брать соответствующие книжки и внимательно их читать, разницей между Паскалем и Си это не определяется.

Если же исходный вопрос задан общего любопытства ради (подозреваю, что это так и есть), то проще не пытаться на пальцах объяснять достаточно сложные вещи и ограничиться утверждением, что "это нормально". :)
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Программирование на Си

Сообщение drBatty »

Фантом писал(а):
22.09.2013 23:58
в природе существует целая наука под названием "вычислительная математика". Со своими проблемами, методами и т.д. И если ТС эта проблема волнует всерьез, то надо брать соответствующие книжки и внимательно их читать, разницей между Паскалем и Си это не определяется.

это верно. Но в сишечке тоже есть своя специфика. И с этой т.з. тоже. ТС уже понял, что размер нигде не задан(точность и т.д.), но ещё не осознал, что это не баг, а фича.

Фантом писал(а):
22.09.2013 23:58
то проще не пытаться на пальцах объяснять достаточно сложные вещи и ограничиться утверждением, что "это нормально".

ну не слишком-то они и сложные ИМХО. Просто наверное слегка непривычные для того, кто с этим не сталкивался. Даже программисты почему-то считают, что компьютеры ВСЁ и ВСЕГДА делают ТОЧНО. Но это так только на первый взгляд. На деле, даже два числа точно сложить не получается. Ошибка есть, хотя и не такая, как в школьных упражнениях.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: Программирование на Си

Сообщение eddy »

drBatty, ну ты загнул с погрешностями! Если у тебя есть 100 измерений абсолютно одной и той же неизменной величины с точностью X%, то среднее (а лучше - медиана) будет иметь точность X/10%. Почитай Гмурмана, что ли!
А вот ежели ты начнешь перемножать 100 случайных величин, имеющих точность X%, то действительно результирующая погрешность будет в 100 раз больше.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
Voral
Сообщения: 1205
ОС: Debian Wheezy (amd64)

Re: Программирование на Си

Сообщение Voral »

drBatty писал(а):
22.09.2013 21:13
А в бухгалтерии вам вообще зачем дробные-то? Целых разве не хватает?

Просто приведу пример из практики: начисление за некую коммунальную услугу: тариф рубли и копейки. 12 числа смена тарифа. 14 у абонента прописался еще человек, а 15 увеличилась площадь, 21 появился льготник, с 18 по 25 услуга не была предоставлена в полном объеме, а абонент оплатил частично или за несколько периодов и т.д и т.п. куча таких различных ситуаций.... А теперь перекиньте это на количество абонентов в более-менее крупном областном центре... Далее идет это все в бухгалтерию где всякий налоги о отчисления в процентах.......
То что не убивает нас, делает нас сильнее! © Ницше.
When life puts you in tough situations, don’t say "why me". Just say "try me © ?
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Программирование на Си

Сообщение drBatty »

eddy писал(а):
23.09.2013 08:45
ну ты загнул с погрешностями! Если у тебя есть 100 измерений абсолютно одной и той же неизменной величины с точностью X%, то среднее (а лучше - медиана) будет иметь точность X/10%. Почитай Гмурмана, что ли!

я читал. И ты перечитай. Есть средний случай, есть медиана, а есть худший случай. Про него ты, как всегда, забыл. А я -- помню.
eddy писал(а):
23.09.2013 08:45
А вот ежели ты начнешь перемножать 100 случайных величин, имеющих точность X%, то действительно результирующая погрешность будет в 100 раз больше.

ВНЕЗАПНО: ты не прав. Да, погрешность будет в 100 раз больше, но и сама величина тоже будет в X^100 раз больше. Например, если ты умножаешь двойки 100 раз, то получится 2^100. А вот погрешность, ЧСХ, растёт как логарифм в среднем для умножения (это и не удивительно, ибо мы отсекаем у флоата всегда с 23го байта, т.е. теряем степенную часть числа).

Однако ты прав в том, что при многократном умножении N раз медиана погрешности увеличивается тоже в N раз. А вот при сложении N раз, в N раз погрешность растёт исключительно в самом поганом случае. К счастью, это касается лишь абсолютной погрешности, а на практике нужна относительная. Потому и float'ы годны и для умножений, в т.ч. и многократных. Относительная точность не теряется, ибо определяется мантиссой, а мантисса в float'е всегда в 23 бита, что ты с ним не делай. (в смысле -- float можно складывать, умножать, вычитать и делить. При этом точность НЕ страдает).
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Программирование на Си

Сообщение drBatty »

Voral писал(а):
23.09.2013 11:45
Просто приведу пример из практики: начисление за некую коммунальную услугу: тариф рубли и копейки. 12 числа смена тарифа. 14 у абонента прописался еще человек, а 15 увеличилась площадь, 21 появился льготник, с 18 по 25 услуга не была предоставлена в полном объеме, а абонент оплатил частично или за несколько периодов и т.д и т.п. куча таких различных ситуаций.... А теперь перекиньте это на количество абонентов в более-менее крупном областном центре...

Да хоть в масштабах Галактики. Я понял задачу: тут требуется использовать некое внутреннее представление. В данном случае разумно всё считать в целых числах:
1. деньги надо считать в копейках. Я думаю, вы понимаете, как это переводить.
2. время надо считать в сутках, ибо, AFAIK, при таких платежах, половина суток == сутки.
3. тариф надо считать в виде рациональной дроби, в числители деньги, в знаменателе время.
Тогда задача сводится к рациональным дробям. Ответ получается тоже в виде дроби, но я думаю, перевести их в привычные копейки за последний месяц вам не составит труда?

При этом, никаких округлений никогда не происходит. Надо только следить за тем, что-бы вычисления не были слишком длинными и слишком сложными. Иначе вы рискуете получить переполнение, и как следствие fuckup. Стандартное решение -- искать НОД дроби и сокращать на него. Если этого недостаточно, возможен поиск наиболее близкой дроби с меньшими числителем и знаменателем. Обе эти задачи обсосаны в глубокой древности. Причём в масштабах вашего областного центра, и при использовании 32х битных целых, для приближения нет никакой нужды (хотя в программе я-бы этот момент всё равно предусмотрел, либо строго доказал, что он невозможен)
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Программирование на Си

Сообщение drBatty »

Voral писал(а):
23.09.2013 11:45
Далее идет это все в бухгалтерию где всякий налоги о отчисления в процентах.......

процент == рациональная дробь. Не вижу проблемы. Или в вашей бухгалтерии уже ввели сложные проценты? (финансисты называют "сложным процентом" любой процент, который зависит от суммы. В нём нет ничего сложного, если ставка задана на год (чем дольше, тем лучше банку, и хуже вкладчику). По настоящему "сложным" является такой процент, который начисляется непрерывно, а не раз в году. Тут рациональными дробями не обойтись, но к счастью программистов, банки такие проценты тоже не любят(они вычисляются с помощью натуральной экспоненты))
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали: