Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
KellyLynch
Member

Откуда:
Сообщений: 45
Я наивно думал, что чем больше в decimal отведено на “количество разрядов, которое может быть расположено справа от десятичного разделителя”, тем точнее будут вычисления.
То есть я ожидал что тип “decimal (20, 11)” даст бОльшую (или уж по крайней мере не меньшую) точность чем decimal (18, 9).

Был у меня кусок программы (SQL Server 2012) вроде такого

CREATE TABLE #Tmp (
		AmountToRelease decimal (18, 9) 
		,ShareToRelease decimal (18, 9) 
		,ShareToRelease1stApproach decimal (18, 9) 
		,Dest decimal (18, 9) 
)

INSERT INTO #Tmp
	(	
		AmountToRelease
		,ShareToRelease
		,ShareToRelease1stApproach
	)
VALUES
	(   0.823673237,
		0.123456781,
		100.987654329
	)

UPDATE #Tmp
	SET Dest =  AmountToRelease  * (ShareToRelease / ShareToRelease1stApproach)

select * from #Tmp

drop table #Tmp


Он дал результат в поле #Tmp.Dest = 0.001006935.

“Абсолютно точный” же результат должен быть “0.823673237 * (0.123456781 / 100.987654329) = 0.0010069354230626”.

Поэтому я решил увеличить точность всех decimal с (18, 9) на (20, 11)
и переписал код так:

CREATE TABLE #Tmp (
		AmountToRelease decimal (20, 11) 
		,ShareToRelease decimal (20, 11) 
		,ShareToRelease1stApproach decimal (20, 11) 
		,Dest decimal (20, 11)  
)

INSERT INTO #Tmp
	(	
		AmountToRelease
		,ShareToRelease
		,ShareToRelease1stApproach
	)
VALUES
	(   0.823673237,
		0.123456781,
		100.987654329
	)

UPDATE #Tmp
	SET Dest =  AmountToRelease  * (ShareToRelease / ShareToRelease1stApproach)

select * from #Tmp

drop table #Tmp


Он дал результат в поле #Tmp.Dest = 0.00100694000.

То есть это даже хуже чем было при decimal (18, 9).

Почему? И что с этим делать?
6 окт 17, 14:16    [20848362]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
TaPaK
Member

Откуда: Kiev
Сообщений: 3604
KellyLynch,

SET Dest = AmountToRelease* CAST((ShareToRelease / ShareToRelease1stApproach) as FLOAT)
6 окт 17, 14:20    [20848386]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
Владислав Колосов
Member

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

что Вы хотите от компьютера, когда Вы используете дешевый вариант плавающей точки?
6 окт 17, 14:21    [20848394]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
KellyLynch
Member

Откуда:
Сообщений: 45
TaPaK
KellyLynch,

SET Dest = AmountToRelease* CAST((ShareToRelease / ShareToRelease1stApproach) as FLOAT)


Да - это сработало. Точность повысилась; спасибо.

Теперь бы хотелось объяснения этого:
- Почему так происходит?
- Где это задокументировано?
- На каких версиях SQL Server это происходит?
6 окт 17, 14:29    [20848442]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
KellyLynch
Member

Откуда:
Сообщений: 45
Владислав Колосов
KellyLynch,

что Вы хотите от компьютера, когда Вы используете дешевый вариант плавающей точки?


Поясните :-) ; не успел за ходом Вашей мысли....
6 окт 17, 14:30    [20848453]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
Владислав Колосов
Member

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

прочтите в справке, что такое DECIMAL и FLOAT, станет понятно.
6 окт 17, 14:33    [20848474]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
iap
Member

Откуда: Москва
Сообщений: 44581
KellyLynch
Где это задокументировано?
Точность, масштаб и длина

Не уверен, то ли это, что вы хотите.
6 окт 17, 14:36    [20848492]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
TaPaK
Member

Откуда: Kiev
Сообщений: 3604
iap
KellyLynch
Где это задокументировано?
Точность, масштаб и длина

Не уверен, то ли это, что вы хотите.

ага, оно, забыл как спрашивать :)
6 окт 17, 14:39    [20848512]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
KellyLynch
Member

Откуда:
Сообщений: 45
TaPaK
iap
пропущено...
Точность, масштаб и длина

Не уверен, то ли это, что вы хотите.

ага, оно, забыл как спрашивать :)


Спасибо, почитаю.

А пока не могли бы Вы ответить на вопрос - а может, вариант ниже дал бы ещё бОльшую точность чем Ваш?:

SET Dest = AmountToRelease * (CAST(ShareToRelease as float) / CAST(ShareToRelease1stApproach as float))
6 окт 17, 14:47    [20848560]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
TaPaK
Member

Откуда: Kiev
Сообщений: 3604
KellyLynch,

т.е. статью вы не октрыли?
6 окт 17, 14:51    [20848586]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
KellyLynch
Member

Откуда:
Сообщений: 45
TaPaK
KellyLynch,

т.е. статью вы не октрыли?



Читал, и не один раз :-); просто долго не мог соотнести то что я прочитал – с исходным кодом “SET Dest = AmountToRelease * (ShareToRelease / ShareToRelease1stApproach)” и исправленным “SET Dest = AmountToRelease* CAST((ShareToRelease / ShareToRelease1stApproach) as FLOAT)”.

Сейчас вроде сообразил; но не вполне уверен.

Не могли бы Вы проверить правильность моих рассуждений:

1. Понял – почему увеличение точности моих полей (с decimal (18, 9) до decimal (20, 11) наоборот, приводило к “ухудшению результата вычислений”. Потому что происходила ситуация, о которой говорится в статье как “* Точность и масштаб результата имеют абсолютный максимум, равный 38. Если значение точности превышает 38, то соответствующий масштаб уменьшается, чтобы, по возможности, предотвратить усечение интегральной части результата.

Поэтому например при умножении двух decimal (18, 9) этой ситуации не происходило: “ Точность результата” была p1 + p2 + 1 = 18 + 18 + 1 = 37 что меньше 38.

А вот если умножить два decimal (20, 11), то : “ Точность результата” высчитывалась как p1 + p2 + 1 = 20+ 20+ 1 = 41, что больше 38. В этом случае SQL Server делал то что названо “соответствующий масштаб уменьшается”; из-за чего у меня “терялись” числа справа от десятичного разделителя

2. Понял – почему “SET Dest = AmountToRelease* CAST((ShareToRelease / ShareToRelease1stApproach) as FLOAT)” стал давать лучший результат. Так как “CAST((ShareToRelease / ShareToRelease1stApproach) as FLOAT” есть FLOAT, то и “AmountToRelease* CAST((ShareToRelease / ShareToRelease1stApproach) as FLOAT)” стал считаться как произведение двух FLOAT-ов (которое только потом, при записи в поле decimal (20, 11), приводилось к decimal-у). То есть здесь не происходило той “потери”, каковая случалась в пункте 1.

Поэтому результат оказывался лучше.

3. А теперь самый сложный вопрос – а есть ли смысл ещё улучшить “SET Dest = AmountToRelease* CAST((ShareToRelease / ShareToRelease1stApproach) as FLOAT)”, сделав из него “SET Dest = AmountToRelease * (CAST(ShareToRelease as float) / CAST(ShareToRelease1stApproach as float))”?

Мне кажется что есть. Сейчас (“CAST((ShareToRelease / ShareToRelease1stApproach) as FLOAT”) сначала считается деление decimal (20, 11) на decimal (20, 11); и только потом оно преобразуется к FLOAT.

“Деление decimal (20, 11) на decimal (20, 11)” ведёт к тому что (как сказано в статье) “Точность результата” определится как p1 - s1 + s2 + max(6, s1 + p2 + 1) = 20 - 11 + 11 + max(6, 11 + 20 + 1) = 52. Это больше чем 38; то есть и тут будет иметь место операция “соответствующий масштаб уменьшается, чтобы, по возможности, предотвратить усечение интегральной части результата”.

А если сделать “SET Dest = AmountToRelease * (CAST(ShareToRelease as float) / CAST(ShareToRelease1stApproach as float))”, то сразу будет делиться FLOAT на FLOAT; и операции "соответствующий масштаб уменьшается, чтобы, по возможности, предотвратить усечение интегральной части результата” не случится.

Что должно дать “ещё лучший” результат.

Ну – какие Вы нашли (если нашли) ошибки в этих рассуждениях?
6 окт 17, 17:16    [20849105]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
TaPaK
Member

Откуда: Kiev
Сообщений: 3604
KellyLynch,

теперь читайте приоритет операци, и приоритет типов.
6 окт 17, 17:18    [20849109]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
TaPaK
Member

Откуда: Kiev
Сообщений: 3604
и фиг я вам дам рыбу :) учитесь ловить
6 окт 17, 17:19    [20849110]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
TaPaK
Member

Откуда: Kiev
Сообщений: 3604
и для данного примера достаточно
автор
SET Dest = CAST(AmountToRelease as FLOAT) * ShareToRelease /ShareToRelease1stApproach

выше был пример просто из привычки не разбираю всю матиматику
6 окт 17, 17:23    [20849128]     Ответить | Цитировать Сообщить модератору
 Re: decimal (20, 11) даёт МЕНЕЕ точный результат чем decimal (18, 9)  [new]
KellyLynch
Member

Откуда:
Сообщений: 45
TaPaK
и для данного примера достаточно
автор
SET Dest = CAST(AmountToRelease as FLOAT) * ShareToRelease /ShareToRelease1stApproach

выше был пример просто из привычки не разбираю всю матиматику


О – спасибо, это то что надо.

В свою очередь я вижу что есть смысл увеличить исходный тип (в моей таблице #Tmp) с decimal (18, 9) даже не до decimal (20, 11) (как я думал сначала), а до decimal (26, 17).
Почему не больше? Потому что MSDN https://docs.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql говорит

“Conversion of float values that use scientific notation to decimal or numeric is restricted to values of precision 17 digits only. Any value with precision higher than 17 rounds to zero.”

То есть делать не decimal (26, 17) а decimal (27, 18) уже не будет смысла.

Я попробовал, и получил вот что:

“Абсолютно точный” результат (посчитанный на Windows Calculator) должен быть “0.823673237 * 0.123456781 / 100.987654329 = 0.00100693542306258884687437406337”.
.
Результат с использованием Вашего кода “SET Dest = CAST(AmountToRelease as FLOAT) * ShareToRelease /ShareToRelease1stApproach” и с decimal (20, 11) даёт 0.00100693542.

Результат с использованием Вашего кода “SET Dest = CAST(AmountToRelease as FLOAT) * ShareToRelease /ShareToRelease1stApproach” и с decimal (26, 17) даёт 0.00100693542306259.

Ну – кажется, сделано всё что можно, чтобы получить “наивозможно более точный результат”.
7 окт 17, 21:58    [20851417]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить