On-line: гостей 2. Всего: 2 [подробнее..]
АвторСообщение





Пост 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
не ноль


Спасибо: 0 
ПрофильЦитата Ответить
Новых ответов нет , стр: 1 2 All [см. все]


постоянный участник




Пост N: 1018
Зарегистрирован: 12.09.06
ссылка на сообщение  Отправлено: 02.01.10 12:10. Заголовок: sergey5703 пишет: I..


sergey5703 пишет:

 цитата:
IF n10 == 0



Попробуй IF n10 == 0.0

Спасибо: 1 
ПрофильЦитата Ответить





Пост N: 9
Зарегистрирован: 01.01.10
ссылка на сообщение  Отправлено: 02.01.10 21:15. Заголовок: Andrey пишет: Попро..


Andrey пишет:

 цитата:
Попробуй IF n10 == 0.0


Попробовал - результат не изменился!


Спасибо: 0 
ПрофильЦитата Ответить
Администратор




Пост 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

точность сравнения подобрать самому по вкусу


Спасибо: 1 
ПрофильЦитата Ответить





Пост N: 11
Зарегистрирован: 01.01.10
ссылка на сообщение  Отправлено: 03.01.10 00:59. Заголовок: Вы абсолютно правы -..


Вы абсолютно правы - все дело в арифметике с плавающей точкой. Вы уж извините старого чудака - решил задать "вопрос на засыпку", хотя ответ на него я лично узнал после нескольких лет программирования на Клиппере, когда программировал задачу по распределению чего-то там на три декады месяца. В этой задаче печатался отчет и я чтобы не засорять отчет нулями вставил проверку на ноль и тогда печатал пробелы. Каково же было мое удивление когда отчет все равно печатался с нулями и что самое смешное и в отладчике тоже значение переменной - ноль, а в программе ветвление идет на "не равно нулю". Хотя должен все таки похвалить писишную арифметику и особенно арифметику с плавающей точкой в Клиппере - я знавал дипломированных программистов для которых было откровением узнать, что арифметика с плавающей точкой НЕ ТОЧНА "по определению". А решение этой проблемы простое - как мычание, мы в нашей организации после делений или умножений (с сложением и вычитанием все нормально) просто писали:
n10 := VAL(STR(n10)) и ВСЕ, и больше ничего не надо - теперь НОЛЬ!


Спасибо: 0 
ПрофильЦитата Ответить
Администратор




Пост N: 1276
Зарегистрирован: 23.05.05
ссылка на сообщение  Отправлено: 03.01.10 10:27. Заголовок: :)..


:)

Спасибо: 1 
ПрофильЦитата Ответить



Не зарегистрирован
Зарегистрирован: 01.01.70
ссылка на сообщение  Отправлено: 03.01.10 18:15. Заголовок: "вопрос на засыпку"


Можете попробовать для "простого решения VAL(str()) " 378.70*0.75-284.03 ?

Спасибо: 1 
Цитата Ответить





Пост 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
не ноль
// Вывод - все нормально, никакого "решения" - ни "простого" ни "сложного" не требуется, потому что выводится не ноль и условие проверки выдают "не ноль". Вы вероятно "не просекли" "фишку" - вопрос был в том, что выводился ноль, а для сравнения был не ноль.


Спасибо: 0 
ПрофильЦитата Ответить





Пост 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? Загадка, однако ...


Спасибо: 0 
ПрофильЦитата Ответить





Пост 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

Получается - никакой загадки, просто в Вашей формуле отсутствует ДЕЛЕНИЕ - в этом вся "фишка" ...

Как там у А.С.Пушкина - "О сколько нам открытий чудных готовит просвещенья дух ..."


Спасибо: 0 
ПрофильЦитата Ответить





Пост 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
не ноль


Спасибо: 0 
ПрофильЦитата Ответить





Пост N: 79
Зарегистрирован: 08.07.06
ссылка на сообщение  Отправлено: 09.01.10 20:56. Заголовок: Аффтар, вместо того,..


Аффтар, вместо того, чтобы выносить мозг себе и людям, замени строчку

? nm5

на

? STR(nm5,20,10)

и все встанет на свои места.

Спасибо: 1 
ПрофильЦитата Ответить



Пост 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 соответствующего С-компилятора и бороться с этим не имеет смысла. Для правильности расчетов надо это учитывать.


Спасибо: 1 
ПрофильЦитата Ответить
постоянный участник


Пост N: 783
Зарегистрирован: 09.10.06
ссылка на сообщение  Отправлено: 10.01.10 16:43. Заголовок: TimTim пишет: Таким..


TimTim пишет:

 цитата:
Таким образом, во всех приведенных выше якобы курьёзах заменить в операторе "? <переменная>" на "? STR( <переменная>, 19, 16 )" и все встанет на свои места.


Я думаю, тут все в пылу дискусии забыли о кoманде SET DECIMALS TO nDecimal и ее влиянии на вывод на экран. Позволю себе напомнить, что по умолчанию nDecimal = 2

и все встанет на свои места

Спасибо: 1 
ПрофильЦитата Ответить
постоянный участник




Пост N: 1035
Зарегистрирован: 12.09.06
ссылка на сообщение  Отправлено: 10.01.10 17:50. Заголовок: Петр пишет: о кoман..


Петр пишет:

 цитата:
о кoманде SET DECIMALS TO nDecimal



Точно, Петр ! Я сам хотел об этом написать ...

Спасибо: 1 
ПрофильЦитата Ответить



Пост N: 53
Зарегистрирован: 10.07.07
ссылка на сообщение  Отправлено: 10.01.10 18:53. Заголовок: Безусловно, Петр, дл..


Безусловно, Петр, для выдачи на экран можно и так.
Но параметр nDecimal равный именно 16 ставит все на свои места.

Спасибо: 1 
ПрофильЦитата Ответить





Пост N: 36
Зарегистрирован: 01.01.10
ссылка на сообщение  Отправлено: 11.01.10 02:38. Заголовок: Спасибо большое госп..


Спасибо большое господа, что высказали свое мнение. Все абсолютно правы, вот только в РЕАЛЬНОЙ программистской работе не всегда есть время задуматься над тем, какие числа в программе получатся, тем более при подсчете рублей.копеек. Так что в нашей организации не я один вставлял в "подозрительных" местах программы конструкцию: var := VAL(STR(var)) - лучше заранее "соломки" подстелить!


Спасибо: 0 
ПрофильЦитата Ответить





Пост N: 80
Зарегистрирован: 08.07.06
ссылка на сообщение  Отправлено: 14.01.10 23:52. Заголовок: sergey5703 пишет: т..


sergey5703 пишет:

 цитата:
только в РЕАЛЬНОЙ программистской работе не всегда есть время задуматься над тем, какие числа в программе получатся, тем более при подсчете рублей.копеек. Так что в нашей организации не я один вставлял в "подозрительных" местах программы конструкцию: var := VAL(STR(var)) - лучше заранее "соломки" подстелить!


Не рекомендую категорически.
Иначе при элементарном подсчете суммы накладной, если используется НДС, курс валют с точностью более одного знака после запятой, либо дробные части количества (кг, тонн и тп) - сумма не сойдется НИКОГДА.

Спасибо: 0 
ПрофильЦитата Ответить



Пост N: 68
Зарегистрирован: 29.07.05
ссылка на сообщение  Отправлено: 15.01.10 09:14. Заголовок: Sergy пишет: Не рек..


Sergy пишет:

 цитата:
Не рекомендую категорически.


это понятно
при таком ответе должна быть рекомендуемая стратегия

Спасибо: 0 
ПрофильЦитата Ответить





Пост N: 81
Зарегистрирован: 08.07.06
ссылка на сообщение  Отправлено: 17.01.10 00:33. Заголовок: les пишет: при тако..


les пишет:

 цитата:
при таком ответе должна быть рекомендуемая стратегия



1) задуматься над тем, как, когда и какие числа в программе получаются
2) никогда не использовать усечение, тем более такое, как x:=VAL(STR(x))
3) всегда использовать максимально возможную точность в таблицах, 3 минимум, а лучше 4-5 знаков после запятой (для руб/коп)
4) выводить результаты усечения в документах в полях "сумма", "итого" и тп - только после того, как с макс. точностью просчитан столбец или строка.

вроде как прописные истины...

Спасибо: 0 
ПрофильЦитата Ответить
постоянный участник




Пост N: 132
Зарегистрирован: 24.09.05
ссылка на сообщение  Отправлено: 13.02.10 19:04. Заголовок: сто раз обсуждали)) ..


сто раз обсуждали))

?0.3-(0.1+0.2)

-0.000000000000000055511151231258

Спасибо: 0 
ПрофильЦитата Ответить
Новых ответов нет , стр: 1 2 All [см. все]
Ответ:
1 2 3 4 5 6 7 8 9
большой шрифт малый шрифт надстрочный подстрочный заголовок большой заголовок видео с youtube.com картинка из интернета картинка с компьютера ссылка файл с компьютера русская клавиатура транслитератор  цитата  кавычки моноширинный шрифт моноширинный шрифт горизонтальная линия отступ точка LI бегущая строка оффтопик свернутый текст

показывать это сообщение только модераторам
не делать ссылки активными
Имя, пароль:      зарегистрироваться    
Тему читают:
- участник сейчас на форуме
- участник вне форума
Все даты в формате GMT  3 час. Хитов сегодня: 16
Права: смайлы да, картинки да, шрифты да, голосования нет
аватары да, автозамена ссылок вкл, премодерация откл, правка нет