Автор | Сообщение |
|
| |
Пост N: 7
Зарегистрирован: 01.01.10
|
|
Отправлено: 02.01.10 01:12. Заголовок: Что бы это значило и как с этим бороться?
// Программа LOCAL n10 := 10, n3 := 3, n10div3, i n10div3 := (n10 / n3) FOR i := 1 TO n3 n10 := n10 - n10div3 NEXT ? n10 IF n10 == 0 ? "ноль" ELSE ? "не ноль" ENDIF ? // Результат 0.00 не ноль
|
|
|
Ответов - 29
, стр:
1
2
All
[только новые]
|
|
|
| постоянный участник
|
Пост N: 1018
Зарегистрирован: 12.09.06
|
|
Отправлено: 02.01.10 12:10. Заголовок: sergey5703 пишет: I..
sergey5703 пишет: Попробуй IF n10 == 0.0
|
|
|
|
| |
Пост N: 9
Зарегистрирован: 01.01.10
|
|
Отправлено: 02.01.10 21:15. Заголовок: Andrey пишет: Попро..
Andrey пишет: Попробовал - результат не изменился!
|
|
|
|
| Администратор
|
Пост N: 1275
Зарегистрирован: 23.05.05
|
|
Отправлено: 03.01.10 00:21. Заголовок: Это результат исполь..
Это результат использования арифметики с числами double Ничего с этим не поделаешь, точность представления double ограничена. Такой же результат даст программка на С: HB_FUNC( N10_3 ) { hb_retl((10.0-10.0/3.0-10.0/3.0-10.0/3.0) == 0.0); } Вызов N10_3 даст .F. так работает FPU А лечение традиционное: вместо сравнения с нулем написать функцию: Function Equ(n1, n2) Return Abs(n1-n2) < 0.0001 точность сравнения подобрать самому по вкусу
|
|
|
|
| |
Пост N: 11
Зарегистрирован: 01.01.10
|
|
Отправлено: 03.01.10 00:59. Заголовок: Вы абсолютно правы -..
Вы абсолютно правы - все дело в арифметике с плавающей точкой. Вы уж извините старого чудака - решил задать "вопрос на засыпку", хотя ответ на него я лично узнал после нескольких лет программирования на Клиппере, когда программировал задачу по распределению чего-то там на три декады месяца. В этой задаче печатался отчет и я чтобы не засорять отчет нулями вставил проверку на ноль и тогда печатал пробелы. Каково же было мое удивление когда отчет все равно печатался с нулями и что самое смешное и в отладчике тоже значение переменной - ноль, а в программе ветвление идет на "не равно нулю". Хотя должен все таки похвалить писишную арифметику и особенно арифметику с плавающей точкой в Клиппере - я знавал дипломированных программистов для которых было откровением узнать, что арифметика с плавающей точкой НЕ ТОЧНА "по определению". А решение этой проблемы простое - как мычание, мы в нашей организации после делений или умножений (с сложением и вычитанием все нормально) просто писали: n10 := VAL(STR(n10)) и ВСЕ, и больше ничего не надо - теперь НОЛЬ!
|
|
|
|
| Администратор
|
Пост N: 1276
Зарегистрирован: 23.05.05
|
|
Отправлено: 03.01.10 10:27. Заголовок: :)..
:)
|
|
|
|
| |
Не зарегистрирован
Зарегистрирован: 01.01.70
|
|
Отправлено: 03.01.10 18:15. Заголовок: "вопрос на засыпку"
Можете попробовать для "простого решения VAL(str()) " 378.70*0.75-284.03 ?
|
|
|
|
| |
Пост N: 13
Зарегистрирован: 01.01.10
|
|
Отправлено: 03.01.10 22:28. Заголовок: Можете попробовать
// Программа LOCAL n10 := (378.70 * 0.75) - 284.03 ? n10 IF n10 == 0 ? "ноль" ELSE ? "не ноль" ENDIF ? // Результат -0.0050 не ноль // Вывод - все нормально, никакого "решения" - ни "простого" ни "сложного" не требуется, потому что выводится не ноль и условие проверки выдают "не ноль". Вы вероятно "не просекли" "фишку" - вопрос был в том, что выводился ноль, а для сравнения был не ноль.
|
|
|
|
| |
Пост N: 16
Зарегистрирован: 01.01.10
|
|
Отправлено: 04.01.10 01:05. Заголовок: Попробовал вычислить "эпсилон"
// Вычисление эпсилон LOCAL n10 := 10, nEpsilon := 1, nOldEpsilon nEpsilon := (nEpsilon / n10) nOldEpsilon := nEpsilon DO WHILE (nEpsilon == VAL(STR(nEpsilon))) nOldEpsilon := nEpsilon nEpsilon := (nEpsilon / n10) ENDDO ? "Epsilon = " + ALLTRIM(STR(nOldEpsilon)) ? // Результат Epsilon = 0.01 Получается что 0.001 - выводится как 0.00, а -0.005 как -0.0050? Загадка, однако ...
|
|
|
|
| |
Пост N: 17
Зарегистрирован: 01.01.10
|
|
Отправлено: 04.01.10 01:58. Заголовок: Хорошая мысля - приходит опосля ...
// Вычисление эпсилон LOCAL n10 := 10, nEpsilon := -5, nOldEpsilon nEpsilon := (nEpsilon / n10) nOldEpsilon := nEpsilon DO WHILE (nEpsilon == VAL(STR(nEpsilon))) nOldEpsilon := nEpsilon nEpsilon := (nEpsilon / n10) ENDDO ? "Epsilon = " + ALLTRIM(STR(nOldEpsilon)) ? // Результат Epsilon = -0.05 Получается - никакой загадки, просто в Вашей формуле отсутствует ДЕЛЕНИЕ - в этом вся "фишка" ... Как там у А.С.Пушкина - "О сколько нам открытий чудных готовит просвещенья дух ..."
|
|
|
|
| |
Пост N: 19
Зарегистрирован: 01.01.10
|
|
Отправлено: 04.01.10 02:49. Заголовок: Две тестовых программы с ДЕЛЕНИЯМИ
// Программа 1 LOCAL n10 := 10, nm5 := -5 nm5 := (nm5 / n10) nm5 := (nm5 / n10) nm5 := (nm5 / n10) ? nm5 IF nm5 == 0.0 ? "ноль" ELSE ? "не ноль" ENDIF ? // Результат -0.01 не ноль // Программа 2 LOCAL n10 := 10, nm5 := -4 nm5 := (nm5 / n10) nm5 := (nm5 / n10) nm5 := (nm5 / n10) ? nm5 IF nm5 == 0.0 ? "ноль" ELSE ? "не ноль" ENDIF ? // Результат 0.00 не ноль
|
|
|
|
| |
Пост N: 79
Зарегистрирован: 08.07.06
|
|
Отправлено: 09.01.10 20:56. Заголовок: Аффтар, вместо того,..
Аффтар, вместо того, чтобы выносить мозг себе и людям, замени строчку ? nm5 на ? STR(nm5,20,10) и все встанет на свои места.
|
|
|
|
|
| |
Пост N: 52
Зарегистрирован: 10.07.07
|
|
Отправлено: 10.01.10 13:30. Заголовок: Присоединюсь к Sergy..
Присоединюсь к Sergy. Хочу только немного уточнить. Приведенный выше алгоритм вычисления epsilon несколько озадачил. Вообще говоря, в серьезных математических вычислениях рекомендуют вычислять так называемый epsilon по следующему алгоритму nEpsilon := 1.0 DO WHILE ( 1.0 + nEpsilon > 1.0 ) nOldEpsilon := nEpsilon nEpsilon := nEpsilon / 2 ? nI, ",", nEpsilon ENDDO nOldEpsilon как раз то, что нам нужно. И несмотря на то, что оператор ? nEpsilon начиная с 31 итерации будет выдавать 0.000000000 цикл продолжится и все-таки закончится. Причем итераций будет проделано 53. Поскольку деление было на 2, то это означает, что на мантиссу числа выделено примерно 7 байтов, что в свою очередь соответствует 16 значащим разрядам после запятой для чисел из интервала [-1,1]. Таким образом, во всех приведенных выше якобы курьёзах заменить в операторе "? <переменная>" на "? STR( <переменная>, 19, 16 )" и все встанет на свои места. sergey5703 пишет: цитата: | (с сложением и вычитанием все нормально) |
| И здесь немного неточно. Как раз таки с вычитанием не все нормально. Именно при вычитаниях может возникнуть значительная потеря точности. Ваша первая программа тому подтверждение. Если правильно поставить печать то, n10 будет равно -0.0000000000000009. Я не поленился и провёл аналогичные расчеты на С (компилятор Borland C 5.5) и на Pascal (компилятор Borland Pascal 7.0). Вот что получилось для первого примера с вычитаниями. На Pascal описал все переменные как double и тогда оператор writeln( n10 ) выдал -8.8817841900125E-0016. Как нетрудно видеть соответствует полученному в Clipper. А вот, на С с так же описанными переменными оператор printf( "%f", n10 ) выдает 0.000000, но printf( " 19.16%f", n10 ) выдает результат точно такой же как в Clipper программе. Теперь, мне кажется, стало более понятно откуда у оператора "?" уши растут. Кстати, в [x]Harbour расчеты такие же как в Clipper-е. Итак, это означает, что в Clipper/[x]Harbour все числовые переменные представляют собой числа типа Double соответствующего С-компилятора и бороться с этим не имеет смысла. Для правильности расчетов надо это учитывать.
|
|
|
|
| постоянный участник
|
Пост N: 783
Зарегистрирован: 09.10.06
|
|
Отправлено: 10.01.10 16:43. Заголовок: TimTim пишет: Таким..
TimTim пишет: цитата: | Таким образом, во всех приведенных выше якобы курьёзах заменить в операторе "? <переменная>" на "? STR( <переменная>, 19, 16 )" и все встанет на свои места. |
| Я думаю, тут все в пылу дискусии забыли о кoманде SET DECIMALS TO nDecimal и ее влиянии на вывод на экран. Позволю себе напомнить, что по умолчанию nDecimal = 2 и все встанет на свои места
|
|
|
|
| постоянный участник
|
Пост N: 1035
Зарегистрирован: 12.09.06
|
|
Отправлено: 10.01.10 17:50. Заголовок: Петр пишет: о кoман..
Петр пишет: цитата: | о кoманде SET DECIMALS TO nDecimal |
| Точно, Петр ! Я сам хотел об этом написать ...
|
|
|
|
| |
Пост N: 53
Зарегистрирован: 10.07.07
|
|
Отправлено: 10.01.10 18:53. Заголовок: Безусловно, Петр, дл..
Безусловно, Петр, для выдачи на экран можно и так. Но параметр nDecimal равный именно 16 ставит все на свои места.
|
|
|
|
| |
Пост N: 36
Зарегистрирован: 01.01.10
|
|
Отправлено: 11.01.10 02:38. Заголовок: Спасибо большое госп..
Спасибо большое господа, что высказали свое мнение. Все абсолютно правы, вот только в РЕАЛЬНОЙ программистской работе не всегда есть время задуматься над тем, какие числа в программе получатся, тем более при подсчете рублей.копеек. Так что в нашей организации не я один вставлял в "подозрительных" местах программы конструкцию: var := VAL(STR(var)) - лучше заранее "соломки" подстелить!
|
|
|
|
| |
Пост N: 80
Зарегистрирован: 08.07.06
|
|
Отправлено: 14.01.10 23:52. Заголовок: sergey5703 пишет: т..
sergey5703 пишет: цитата: | только в РЕАЛЬНОЙ программистской работе не всегда есть время задуматься над тем, какие числа в программе получатся, тем более при подсчете рублей.копеек. Так что в нашей организации не я один вставлял в "подозрительных" местах программы конструкцию: var := VAL(STR(var)) - лучше заранее "соломки" подстелить! |
| Не рекомендую категорически. Иначе при элементарном подсчете суммы накладной, если используется НДС, курс валют с точностью более одного знака после запятой, либо дробные части количества (кг, тонн и тп) - сумма не сойдется НИКОГДА.
|
|
|
|
| |
Пост N: 68
Зарегистрирован: 29.07.05
|
|
Отправлено: 15.01.10 09:14. Заголовок: Sergy пишет: Не рек..
Sergy пишет: цитата: | Не рекомендую категорически. |
| это понятно при таком ответе должна быть рекомендуемая стратегия
|
|
|
|
| |
Пост N: 81
Зарегистрирован: 08.07.06
|
|
Отправлено: 17.01.10 00:33. Заголовок: les пишет: при тако..
les пишет: цитата: | при таком ответе должна быть рекомендуемая стратегия |
| 1) задуматься над тем, как, когда и какие числа в программе получаются 2) никогда не использовать усечение, тем более такое, как x:=VAL(STR(x)) 3) всегда использовать максимально возможную точность в таблицах, 3 минимум, а лучше 4-5 знаков после запятой (для руб/коп) 4) выводить результаты усечения в документах в полях "сумма", "итого" и тп - только после того, как с макс. точностью просчитан столбец или строка. вроде как прописные истины...
|
|
|
|
| постоянный участник
|
Пост N: 132
Зарегистрирован: 24.09.05
|
|
Отправлено: 13.02.10 19:04. Заголовок: сто раз обсуждали)) ..
сто раз обсуждали)) ?0.3-(0.1+0.2) -0.000000000000000055511151231258
|
|
|
Ответов - 29
, стр:
1
2
All
[только новые]
|
|