Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / FoxPro, Visual FoxPro Новый топик    Ответить
 непонятки с округлением  [new]
quxix
Guest
День добрый.

запускаю скрипт:
FOR s=0 TO 100000 STEP .01
 IF s+round(s*.18,2)<>round(s*1.18,2)
   ?s,round(s*.18,2),s+round(s*.18,2),round(s*1.18,2)
    exit
 endif
endfor
получаю exit и такую строчку:
18593.25   3346.79  21940.04   21940.03

проверяю вручную в командном окне фокса:
?ROUND(18593.25*1.18,2)
=21940.04
Т.е. округления в программе и вручную не сходятся в чём загвоздка?

Vfp9
13 янв 10, 15:00    [8177930]     Ответить | Цитировать Сообщить модератору
 Re: непонятки с округлением  [new]
AmKad
Member

Откуда:
Сообщений: 5324
quxix,

round(0.3) + round(0.3) <> round(0.6)
13 янв 10, 15:10    [8178035]     Ответить | Цитировать Сообщить модератору
 Re: непонятки с округлением  [new]
quxix
Guest
AmKad
quxix,

round(0.3) + round(0.3) <> round(0.6)

?
13 янв 10, 15:26    [8178249]     Ответить | Цитировать Сообщить модератору
 Re: непонятки с округлением  [new]
AmKad
Member

Откуда:
Сообщений: 5324
AmKad,

Это на всякий случай, что округление суммы и округление слагаемых не одно и тоже :)

Как-то сталкивался с такой проблемой, только вот сейчас не помню как вышел из такой ситуации.
Я бы начал с того что поигрался бы с настройками сделать Set Decimals to и поставить значение большее 2.
13 янв 10, 15:34    [8178321]     Ответить | Цитировать Сообщить модератору
 Re: непонятки с округлением  [new]
AmKad
Member

Откуда:
Сообщений: 5324
quxix,

Жаль что фокса нет под рукой, так бы сам попробовал :)
13 янв 10, 15:39    [8178354]     Ответить | Цитировать Сообщить модератору
 Re: непонятки с округлением  [new]
quxix
Guest
AmKad
AmKad,

Это на всякий случай, что округление суммы и округление слагаемых не одно и тоже :)

Как-то сталкивался с такой проблемой, только вот сейчас не помню как вышел из такой ситуации.
Я бы начал с того что поигрался бы с настройками сделать Set Decimals to и поставить значение большее 2.


Про округление суммы и округление суммы слагаемых и так ясно :)

Вопрос по работе фокса.
Из Help:
Команда SET DECIMALS определяет минимальное количество отображаемых десятичных знаков
Поигрался.При увеличении порог уменьшается->выходим раньше.
Интересует,округление только до двух десятичных.
***
Поигрался на oracle на интервале до 100000-всё пучком нигде не выбрасывает.
Так что, что-то с непониманием настроек фокса,кто бы указал ...
13 янв 10, 16:04    [8178584]     Ответить | Цитировать Сообщить модератору
 Re: непонятки с округлением  [new]
AmKad
Member

Откуда:
Сообщений: 5324
quxix

Команда SET DECIMALS определяет минимальное количество отображаемых десятичных знаков
Поигрался.При увеличении порог уменьшается->выходим раньше.


Если из цикла выходит раньше ввиду того что поменяли set decimals, может быть и стоит не нее обратить внимание?
13 янв 10, 16:24    [8178743]     Ответить | Цитировать Сообщить модератору
 Re: непонятки с округлением  [new]
прошелмимо
Member [заблокирован]

Откуда: Из Курска понаехал
Сообщений: 10363
сделай так
FOR s=0 TO 100000 STEP .01
 s=round(s,2)
 IF s+round(s*.18,2)<>round(s*1.18,2)
   ?s,round(s*.18,2),s+round(s*.18,2),round(s*1.18,2)
    exit
 endif
endfor

его плючит, когда-то проскакивала информация, сейчас лень искать
13 янв 10, 16:36    [8178847]     Ответить | Цитировать Сообщить модератору
 Re: непонятки с округлением  [new]
ВладимирМ
Member

Откуда: г. Москва
Сообщений: 7912
Вы путаете понятия "хранение числа" и "отображение числа".

Ваша ошибка в том, что Вы считатете, что число s в момент срабатывания условия ТОЧНО равно значению 18593.25. На самом деле это не так. В памяти компьютера оно представлено приблизительно

Добавьте Вашу проверку еще одной строчкой

FOR s=0 TO 100000 STEP .01
 IF s+round(s*.18,2)<>round(s*1.18,2)

   DISPLAY MEMORY

   ?s,round(s*.18,2),s+round(s*.18,2),round(s*1.18,2)
    exit
 endif
endfor

Вы увидите, что в момент возникновения проблемы значение s = 18593,24999996

Приблизительно это число действительно равно 18593,25, но на самом деле, разумеется, отличается. Соответственно, при сравнении будет

21940,02999996 <> 21940,03

Причина заключается в том, что действительные числа, физически хранятся в памяти FoxPro в виде бинарных (двоичных) данных. Другими словами, они представлены в памяти FoxPro как число по основанию 2. А точно перевести дробное число по основанию 10 в число по основанию 2 - невозможно.

Для решения проблемы, Вам надо либо работать с целыми числами и только результат делить на 100, либо округлять значение s перед выполнением операции

FOR s=0 TO 10000000 STEP 1
 IF (s+s*18/100)/100<>(s*118/100)/100
   ?s,round(s*18/100,2),s+round(s*18/100,2),round(s*118/100,2)
    exit
 endif
endfor

Или

FOR s=0 TO 100000 STEP 0.01

  s = Round(s,2)

 IF s+round(s*.18,2)<>round(s*1.18,2)
   ?s,round(s*.18,2),s+round(s*.18,2),round(s*1.18,2)
    exit
 endif
endfor
13 янв 10, 16:48    [8178948]     Ответить | Цитировать Сообщить модератору
 Re: непонятки с округлением  [new]
ВладимирМ
Member

Откуда: г. Москва
Сообщений: 7912
Да, без Dislpay Memory можно так

FOR s=0 TO 100000 STEP 0.01
 IF s+round(s*.18,2)<>round(s*1.18,2)
   ?Str(s,20,18)
   ?round(s*.18,2),s+round(s*.18,2),round(s*1.18,2)
    exit
 endif
endfor

Т.е. не доверять автоматическому преобразованию числа в строку, а явно указать, что надо отобразить столько десятичных символов, сколько возможно.
13 янв 10, 16:53    [8178991]     Ответить | Цитировать Сообщить модератору
 Re: непонятки с округлением  [new]
quxix
Guest
ВладимирМ,
спасибо за подробное разъяснение.

Скажите, а почему настройка SET DECIMALS TO -влияет на результат вычисления,в первоночальном скрипте, если я правильно понял, то настройка должна влиять лишь на отображение числа.
14 янв 10, 13:20    [8183309]     Ответить | Цитировать Сообщить модератору
 Re: непонятки с округлением  [new]
ВладимирМ
Member

Откуда: г. Москва
Сообщений: 7912
quxix
Скажите, а почему настройка SET DECIMALS TO -влияет на результат вычисления,в первоночальном скрипте, если я правильно понял, то настройка должна влиять лишь на отображение числа.

Не знаю. Теоретически, не должна, хотя на практике это есть

Clear
For nDec=0 to 5
Set Decimals To m.nDec
?"Set Decimals To",nDec
?"---------------------------------"
	FOR s=0 TO 100000 STEP .01
		IF not (s + Round(s*0.18, 2) == Round(s*1.18, 2))
			?"s=", Str(s,20,18)
			?"s*0.18=", Str(s*0.18,20,18)
			?"round(s*0.18, 2)=", Str(round(s*0.18, 2),20,18)
			?"s+round(s*0.18, 2)=", Str(s+round(s*0.18, 2),20,18)
			?"s*1.18=", Str(s*1.18,20,18)
			?"round(s*1.18, 2)=", Str(round(s*1.18, 2),20,18)
			Exit 
		EndIf
	EndFor 
?"---------------------------------"
EndFor 

Если посмотреть на результат теста, то видно, что выход из цикла происходит в зависимости от того, на какой позиции заканчиваются цифры 9 в значении у функции ROUND(). Чем меньше значение SET DECIMALS, тем меньшее количество девяток необходимо. Тем позднее происходит вылет из цикла. Грубее сравнение.

Например, для SET DECIMALS TO 2 имеем

s = 18593.24999995757000
s*0.18 = 3346.784999992362000
ROUND(s*0.18, 2) = 3346.79	&& т.е. 0,78499999 было округлено до 0,79
s*1.18 = 21940.03499994993000
ROUND(s*1.18, 2) = 21940.034	&& т.е. 0,0349999 было округлено до 0,03

Другими словами, если число равно 0,004999990, то оно округляется до 0,01, а если 0,004999900 (на одну девятку в конце меньше), то оно округляется до 0,00. Причем значение цифры после последней учитываемой девятки значение не имеет. Важно только, чтобы все учитываемые цифры после 4 были именно девятками.

Чем больше настройка SET DECIMALS, тем бОльшее количество девяток учитывается при подобном бухгалтерском округлении. Тем раньше происходит вылет при сравнении, поскольку разница фактически становится равной 0.01

В принципе, такого быть не должно. В смысле, работа ROUND() не должна зависеть от SET DECIMALS. Однако, как видно из примера, зависит...

Впрочем, все это происходит на пределе точности расчета FoxPro (до 16 значащих цифр). Так что, думаю, погрешность вполне допустимая.

--------------------------------------------------------------------------------------------

Если нужны точные расчеты сумм, то лучше вместо полей типа Numeric использовать поле типа Currency, поскольку физически оно хранится как целоей число. Соответственно, нет погрешностей при переводе в двоичное представление.

Преобразование NTOM() как раз и переводит число типа Numeric в число типа Currency. И уже тот же самый цикл никаких ошибок не покажет

Clear
For nDec=0 to 5
Set Decimals To m.nDec
?"Set Decimals To",nDec
?"---------------------------------"
	FOR s=Ntom(0) TO Ntom(100000) STEP Ntom(0.01)
		IF not (s + Round(s*0.18, 2) == Round(s*1.18, 2))
			?"s=", Str(s,20,18)
			?"s*0.18=", Str(s*0.18,20,18)
			?"round(s*0.18, 2)=", Str(round(s*0.18, 2),20,18)
			?"s+round(s*0.18, 2)=", Str(s+round(s*0.18, 2),20,18)
			?"s*1.18=", Str(s*1.18,20,18)
			?"round(s*1.18, 2)=", Str(round(s*1.18, 2),20,18)
			Exit 
		EndIf
	EndFor 
?"---------------------------------"
EndFor 

Однако следует иметь в виду, что математические операции с типом Currency дают точность не выше 4 значащих цифр после запятой. По правилам математики это значит, что 4 цифра - не достоверная, 3 - сомнительная. Т.е. доверять можно будет только первым 2 цифрам после запятой (до копейки).

Также следует помнить, что если тип данных константы не будет задан явно, то дробные числа FoxPro интерпретирует как числа с типом Numeric. Как уже видели, прямое сравнение Numeric и Currency может дать парадоксальный результат из-за особенностей хранения в памяти.
14 янв 10, 15:42    [8184946]     Ответить | Цитировать Сообщить модератору
 Re: непонятки с округлением  [new]
ВладимирМ
Member

Откуда: г. Москва
Сообщений: 7912
Если интересно, то вот по этой ссылке

http://forum.foxclub.ru/read.php?29,401694,page=1

Было обширное обсуждение аналогичной ошибки в функции INT().

А вот здесь

http://support.microsoft.com/kb/78113/ru

Статья от Microsoft с описанием того, почему арифметические операции над числами с плавающей точкой могут быть не точными. Там про Excel, но в FoxPro в отношении Numeric все то же самое
14 янв 10, 16:23    [8185324]     Ответить | Цитировать Сообщить модератору
 Re: непонятки с округлением  [new]
quxix
Guest
ВладимирМ,
ещё раз благодарю Вас, за обширное объяснение.
Спасибо.
15 янв 10, 10:03    [8188403]     Ответить | Цитировать Сообщить модератору
Все форумы / FoxPro, Visual FoxPro Ответить