Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / FoxPro, Visual FoxPro [игнор отключен] [закрыт для гостей] / непонятки с округлением / 14 сообщений из 14, страница 1 из 1
13.01.2010, 15:00
    #36407659
quxix
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
День добрый.

запускаю скрипт:
Код: plaintext
1.
2.
3.
4.
5.
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 и такую строчку:
Код: plaintext
 18593 . 25     3346 . 79    21940 . 04     21940 . 03 

проверяю вручную в командном окне фокса:
Код: plaintext
1.
?ROUND( 18593 . 25 * 1 . 18 , 2 )
= 21940 . 04 
Т.е. округления в программе и вручную не сходятся в чём загвоздка?

Vfp9
...
Рейтинг: 0 / 0
13.01.2010, 15:10
    #36407695
AmKad
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
quxix,

Код: plaintext
1.
round( 0 . 3 ) + round( 0 . 3 ) <> round( 0 . 6 )
...
Рейтинг: 0 / 0
13.01.2010, 15:26
    #36407758
quxix
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
AmKadquxix,

Код: plaintext
1.
round( 0 . 3 ) + round( 0 . 3 ) <> round( 0 . 6 )

?
...
Рейтинг: 0 / 0
13.01.2010, 15:34
    #36407781
AmKad
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
AmKad,

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

Как-то сталкивался с такой проблемой, только вот сейчас не помню как вышел из такой ситуации.
Я бы начал с того что поигрался бы с настройками сделать Set Decimals to и поставить значение большее 2.
...
Рейтинг: 0 / 0
13.01.2010, 15:39
    #36407793
AmKad
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
quxix,

Жаль что фокса нет под рукой, так бы сам попробовал :)
...
Рейтинг: 0 / 0
13.01.2010, 16:04
    #36407888
quxix
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
AmKadAmKad,

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

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

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

Вопрос по работе фокса.
Из Help:
Команда SET DECIMALS определяет минимальное количество отображаемых десятичных знаков
Поигрался.При увеличении порог уменьшается->выходим раньше.
Интересует,округление только до двух десятичных.
***
Поигрался на oracle на интервале до 100000-всё пучком нигде не выбрасывает.
Так что, что-то с непониманием настроек фокса,кто бы указал ...
...
Рейтинг: 0 / 0
13.01.2010, 16:24
    #36407955
AmKad
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
quxix
Команда SET DECIMALS определяет минимальное количество отображаемых десятичных знаков
Поигрался.При увеличении порог уменьшается->выходим раньше.


Если из цикла выходит раньше ввиду того что поменяли set decimals, может быть и стоит не нее обратить внимание?
...
Рейтинг: 0 / 0
13.01.2010, 16:36
    #36407996
прошелмимо
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
сделай так
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
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

его плючит, когда-то проскакивала информация, сейчас лень искать
...
Рейтинг: 0 / 0
13.01.2010, 16:48
    #36408033
ВладимирМ
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
Вы путаете понятия "хранение числа" и "отображение числа".

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

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

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
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 перед выполнением операции

Код: plaintext
1.
2.
3.
4.
5.
6.
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

Или

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
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
...
Рейтинг: 0 / 0
13.01.2010, 16:53
    #36408045
ВладимирМ
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
Да, без Dislpay Memory можно так

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
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

Т.е. не доверять автоматическому преобразованию числа в строку, а явно указать, что надо отобразить столько десятичных символов, сколько возможно.
...
Рейтинг: 0 / 0
14.01.2010, 13:20
    #36409647
quxix
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
ВладимирМ,
спасибо за подробное разъяснение.

Скажите, а почему настройка SET DECIMALS TO -влияет на результат вычисления,в первоночальном скрипте, если я правильно понял, то настройка должна влиять лишь на отображение числа.
...
Рейтинг: 0 / 0
14.01.2010, 15:42
    #36410148
ВладимирМ
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
quxixСкажите, а почему настройка SET DECIMALS TO -влияет на результат вычисления,в первоночальном скрипте, если я правильно понял, то настройка должна влиять лишь на отображение числа.
Не знаю. Теоретически, не должна, хотя на практике это есть

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
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 имеем

Код: plaintext
1.
2.
3.
4.
5.
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. И уже тот же самый цикл никаких ошибок не покажет

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
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 может дать парадоксальный результат из-за особенностей хранения в памяти.
...
Рейтинг: 0 / 0
14.01.2010, 16:23
    #36410285
ВладимирМ
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
Если интересно, то вот по этой ссылке

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

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

А вот здесь

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

Статья от Microsoft с описанием того, почему арифметические операции над числами с плавающей точкой могут быть не точными. Там про Excel, но в FoxPro в отношении Numeric все то же самое
...
Рейтинг: 0 / 0
15.01.2010, 10:03
    #36411466
quxix
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
непонятки с округлением
ВладимирМ,
ещё раз благодарю Вас, за обширное объяснение.
Спасибо.
...
Рейтинг: 0 / 0
Форумы / FoxPro, Visual FoxPro [игнор отключен] [закрыт для гостей] / непонятки с округлением / 14 сообщений из 14, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]