Странная типизация результата

Страницы: 1
RSS
Странная типизация результата
 
Возник очень любопытный и очень неприятный эффект. Суть в следующем.
Запускаю один единственный скрипт, открыт один график, открыт один источник данных. Сессия закрыта, помех связи нет. Дальше самое интересное.
Есть функция, назовем ее func() такого вида:

local function func()
local x, y, z, param
-- Присваиваются значения переменным y, z, param. Все значения - целочисленные (integer).
x = (y-z)*param  -- результат в x тоже целочисленный
-- некоторые действия, которые не изменяют значений всех четырех переменных
-- Вторично присваиваются значения переменным y, z, param. Все значения - целочисленные (integer).
x = (y-z)*param  -- снова результат в x тоже целочисленный
-- некоторые действия, которые не изменяют значений всех четырех переменных
-- Снова (уже в третий раз) присваиваются значения переменным y, z, param. Все значения - целочисленные (integer).
x = (y-z)*param  
-- результат в x оказывается числом с плавающей точкой (double). Ошибка в расчетах!
end

Эффект проявился впервые. В документации по Lua сказано, что интерпретатор знает, какой должен быть тип у результата арифметической операции, в зависимости от операнда. Здесь же получается, что все операнды - целочисленные, но результат или получается или остается типа double.
Сооружать костыли в виде постоянного использования math.floor как-то не хочется.
Есть ли у кого помимо разработчиков какие-то соображения, почему такое могло случиться? Были ли у кого подобные "фокусы".

PS.
Была бы в Lua четкая типизация данных, такого эффекта не могло бы быть в принципе.
Жаль, что все идет в виде union.
 
Андрей.

в луа нет типа integer. Тип значения number плавающий с точкой.
www.bot4sale.ru

Пасхалочка для Алексея Иванникова: https://forum.quik.ru/messages/forum10/message63088/topic7052/#message63088
 
Михаил, это понятно. Относительно операций, где присутствует, хотя бы теоретическая возможность генерации результата типа double, у меня нет никаких вопросов. Но в описании Lua, в частности, самого Р.Иерусалимски, указано, что условно для пользователя тип конкретной переменной определяется значением, присваиваемым "на лету".
Т.е., пишу я, скажем, x = 3. Да, по факту оно хранится как double (допустим, как 3,000000000001). Но для пользователя это трактуется как 3. И если я проведу сравнение (x == 3), то получу значение true, а не false, как было бы в случае с чистым double/
Если исходить из того, что "условно целочисленное представление" не работает в большинстве случаев с присваиванием чистых значений integer, то тогда вся арифметика, мобильность и логические отношения в Lua летят вверх тормашками.

У меня есть очень сильное подозрение, по аналогии с целым рядом других ситуаций, что эффект, подобный описанному мною, получается из-за ориентированности терминала QUIK на нормальную работу скриптов небольшого размера. Возможно, проблема в накоплении каких-то погрешностей, где-то, может быть, запоздал вызов сборшика мусора. Вобщем, что-то тут есть.
По моим исследованиям, как только скрипт (основной плюс подключаемые через dofile), превышает суммарно некоторый объем, при выполнении скрипта начинаются какие-то совершенно несуразные ошибки.

В этой связи, хочу еще спросить у разработчиков: какой суммарный объем скрипта в килобайтах обеспечивает безошибочную работу в рамках терминала QUIK? То, что есть предел определенный - несомненно. Хотелось бы его узнать поточнее.
А также еще вопрос: с какой периодичностью при выполнении скрипта QUIKв фоновом режиме запускает сборщик мусора?
 
Можно руководствоваться следующими принципами.

Присваивание всегда делается точно.

i = 3
i == 3 после этого всегда даст true

арифметика плюс/минус между целыми числами, хранчщимтся в формате double, в 99.99 случаях даст целое, если сумма не представится большим числом, в котором будет переполнение мантиссы.

умножение и деление, не говоря уже о всяких корнях, в большинстве случаев уже даст накопившуюся ошибку.
www.bot4sale.ru

Пасхалочка для Алексея Иванникова: https://forum.quik.ru/messages/forum10/message63088/topic7052/#message63088
 
Цитата
Andrei2016 написал: Т.е., пишу я, скажем, x = 3. Да, по факту оно хранится как double (допустим, как 3,000000000001). Но для пользователя это трактуется как 3. И если я проведу сравнение (x == 3), то получу значение true, а не false, как было бы в случае с чистым double/
Из того, что вы пишите, видно, что вы заблуждаетесь (про связь размера скрипта и точности вычислений - вообще нонсенс).  Но чтобы вам это объяснить нужно, чтобы вы сначала показали конктерные цифры, с которыми вы оперировали, и как именно вы определяли, что в последнем x - double, а не "integer".
 
^ что-то пошло не так (видимо, слеш в конце цитаты), жалко, что нет возможности редактировать посты.

Цитата
s_mike@rambler.ru написал:
умножение и деление, не говоря уже о всяких корнях, в большинстве случаев уже даст накопившуюся ошибку.
Умножение целого на целое ошибки не даст (опять же, если результат влезает в +-2^54, как вы правильно заметили).  Я думаю, что Andrei2016 просто ошибается либо в том, что на входе в x = (y-z)*param у него целые, либо в том, что на выходе не целое.  Это вообще типично: приходит человек, и делает утверждение, "Я написал скрипт, и абсолютно уверен, что он правильный.  А результат - неверный!  Скажите, это что не работает: Lua, Quik, или компьютер?"

Цитата
Andrei2016 написал:
По моим исследованиям, как только скрипт (основной плюс подключаемые через dofile), превышает суммарно некоторый объем, при выполнении скрипта начинаются какие-то совершенно несуразные ошибки.

В этой связи, хочу еще спросить у разработчиков: какой суммарный объем скрипта в килобайтах обеспечивает безошибочную работу в рамках терминала QUIK? То, что есть предел определенный - несомненно.
Предлагаю рассмотреть другую гипотизу: вы пишите небольшой скрипт, делаете небольшое количество ошибок, все более-менее работает.  Вы пишите большой скрипт, делаете большое количество ошибок - все перестает работать.  Уверенность в отсутствии ошибок в собственном коде - основная помеха их исправлению...
 
Цитата
kroki написал:
если результат влезает в +-2^54
поправлю, в ±2^53, если быть более точным
 
Цитата
Suntor написал:
поправлю, в ±2^53, если быть более точным
Точно, 52+1.  И "гипотеза" через "е" (и может что еще).

А ведь мог бы сказать, что "у меня было все правильно, но в браузере ошибка, и при передаче текста он..." :)
 
kroki,
проверяется элементарно через message("x= "..x.."  y= "..y.."  z= "..z.."  param= "..param). Можете убедиться сами.
Речь идет не о сложном или простом коде. Речь идет о том, что происходит одно и то же присваивание результата от оперирования целочисленными значениями три раза: дважды результат - целочисленный, а на третий раз - с плавающей точкой. Вопрос: каким образом это происходит? Повторюсь: нет никаких сторонних операндов или странных преобразований.

Относительно повышения ошибочности скрипта при превышении определенного объема.
К сожалению, не правы вы. Самая простая вещь: присвоение значения элементу таблицы и затем адресация при скрипте , скажем в 30К работает как и должно. При скрипте объемом 300К вы начинаете получать ошибку ровно в том же самом месте. Я это уже проходил, и даже писал об этом на форуме.
При этом заметьте: я веду речь не просто о stand-alone Lua, а именно о трансляции скрипта под управлением QUIK.
 
Цитата
Andrei2016 написал:
проверяется элементарно через message("x= "..x.."  y= "..y.."  z= "..z.."  param= "..param). Можете убедиться сами.
Проверил.  При переводе числа в строку Lua по умолчанию округляет до 14 знаков (ну и отбрасывает хвостовые нули, формат "%.14g").  Однако в double знаков, имеющих значение - 17.  Поэтому то, что в выдаче message() вы видите как "целое" число, на самом деле таковым может и не являться.  Можете поверить:
Код
local x = 1.000000000000007
if x ~= 1 then
   message("x="..x..", real x="..string.format("%.17g", x))
end
Сообщение будет выведено, ибо x не равен 1.  Но в самом сообщении в "элементарной" его части будет написано, что равен (в правильной - реальное значение, которое не равно и исходному, ибо оно не представимо точно).  Вооружившись этим знанием, перепроверьте все ваши скрипты по 300K - возможно, найдете и другие заблуждения, и вторая проблема тоже уйдет ;) (я с ней помочь не смогу (думаю, что и другие тоже), но стою на своем: ошибка у вас, не в Lua - и вам только кажется, что факт запуска Lua под Quik что-то меняет в плане корректности выполнения).
 
kroki,
проблема в том, что эффект проявился на 11-м знаке, а не на 17-м:
вместо 2 message выдал 1.999999999991. Это первое.
Второе. Я же четко написал: присваивается операнду ЦЕЛОЧИСЛЕННОЕ значение, т.е. условно "x=10".
Вариант, который вы приводите в качестве аргумента - "local x = 1.000000000000007", изначально содержит число с плавающей точкой, а не целочисленное. Посему к моему случаю ваш пример отношения не имеет.
Относительно остального: речь в принципе идет не о логике вычислений и не об ошибках алгоритма, а о результатах одной и той же простейшей операции с технической точки зрения. Мои опыты показали, при падении объема скрипта до уровня примерно 50К-60К (просто за счет физического убирания нескольких процедур, при оставлении неизменным содержимого сбойной операции), все становится идеально.
То, что QLua в плане своей "аппаратной" (назовем так) реализации существенно отличается от того же Lua for Windows, - это уже и проверенный, и доказанный не только мною факт. Зачем опровергать очеивдное?
 
Цитата
Andrei2016 написал:
kroki,
проблема в том, что эффект проявился на 11-м знаке, а не на 17-м:
вместо 2 message выдал 1.999999999991. Это первое.
Второе. Я же четко написал: присваивается операнду ЦЕЛОЧИСЛЕННОЕ значение, т.е. условно "x=10".
Выложите сюда фрагмент кода с ошибкой, чтобы мы смогли его запустить у себя на Quik и воссоздать ситуацию. Тогда быстрее поможем найти ошибку...
 
Цитата
Andrei2016 написал:
проблема в том, что эффект проявился на 11-м знаке, а не на 17-м:
вместо 2 message выдал 1.999999999991. Это первое.
Происходит следующее: у вас после 14-го знака есть дробная часть, но вы своим "элементарным" тестом ее не видите из-за округления.  После умножения на param эта дробная часть передвигается в 11-й знак, вы начинаете ее видеть и дивиться.

Цитата
Второе. Я же четко написал: присваивается операнду ЦЕЛОЧИСЛЕННОЕ значение, т.е. условно "x=10".
Нет, вы вполне "четко" написали, что вы думаете, что присваивается целое, а на вопрос, почему вы так думаете, вы показали неверную проверку.  Вы думаете, что
Цитата
Andrei2016 написал:
Т.е., пишу я, скажем, x = 3. Да, по факту оно хранится как double (допустим, как 3,000000000001). Но для пользователя это трактуется как 3.
а это вообще нонсенс.  Фактически вы и утверждаете, что в вашей терминологии 3,000000000001 - целое.  Поэтому не нужно никаких "условно x=10", прямо перед строкой x = (y-z)*param сделайте присвоение своих целых значений всем переменным, и убедитесь, что результат - тоже целое.  Или проверьте, что y == math.floor(y) (и также для z и param).  Ну или покажите эти числа, как вам уже дважды предложили.  Это, конечно, если вы хотите решить свою проблему, а не продолжать заблуждаться, будто компьютер вас обманывает...
 
Забыл про эту часть:
Цитата
Andrei2016 написал:
Мои опыты показали, при падении объема скрипта до уровня примерно 50К-60К (просто за счет физического убирания нескольких процедур, при оставлении неизменным содержимого сбойной операции), все становится идеально.
То, что QLua в плане своей "аппаратной" (назовем так) реализации существенно отличается от того же Lua for Windows, - это уже и проверенный, и доказанный не только мною факт. Зачем опровергать очеивдное?
Ммм, хотя что тут можно сказать?  В МИД Англии или США вас ждет прекрасное будушее :).  На наших же просторах доказательства нужно приводить, а не только утверждать, что они у вас есть.  Есть что-то там "очевидно" - пожалуйста, покажите.
 
kroki,
почитайте на досуге товарища Р.Иерусалимски - создателя Lua - относительно типизации значений и переменных в Lua. И тогда все ваши рассуждения по поводу 14, 17 и 19-го знаков пойдут лесом.
Вам русским языком говорят: ПРИСВАИВАЕТСЯ ЦЕЛОЧИСЛЕННОЕ значение. Понимаете: ПРИСВАИВАЕТСЯ, а не предполагается. Если вам это непонятно, то говорить не о чем.
По поводу очевидных доказательств. Вы предлагаете мне запостить на форуме порядка 200К программного кода? Можете заняться этим сами, если ARQA позволит. Кусок кода, где проявился эффект я привел. Можете искать очевидное или невероятное сколько влезет. Меня же интересовало мнение других, сталкивался кто-либо с таким эффектом или нет. Кто смог сказать что-то по делу, сказал.
 
Цитата
Suntor написал:
Цитата
Andrei2016   написал:
kroki,
проблема в том, что эффект проявился на 11-м знаке, а не на 17-м:
вместо 2 message выдал 1.999999999991. Это первое.
Второе. Я же четко написал: присваивается операнду ЦЕЛОЧИСЛЕННОЕ значение, т.е. условно "x=10".
Выложите сюда фрагмент кода с ошибкой, чтобы мы смогли его запустить у себя на Quik и воссоздать ситуацию. Тогда быстрее поможем найти ошибку...

Suntor, так я привел его в своем первом сообщении. В том-то и дело, что ничего подобного такому эффекту быть не должно. Поэтомуречь и идет не об ошибке, а об эффекте. Меня же интересовал вопрос, из-за чего может проявиться такой эффект: дважды после присаивания значения integer все происходит идеально, на третий раз - результат не целочисленный, а с плавающей точкой.
Как я понял, ни у кого подобный эффект не проявлялся. Ну. на нет - и суда нет.
Кстати, "подпорку" для решения вопроса я уже соорудил. Вы будете долго смеяться, когда узнаете как. :)
Решение оказалось не менее странным, чем сам эффект:
Перед третьим присваиванием "x = (y-z)*param" нужно добавить еще одно присваивание, скажем так:
"x=10". Все - на этом эффект числа с плавающей точкой в x исчезает, и message выводит нормальное 2.
А теперь, если вы уберете "подпорку", эффект double возвращается на место.
 
Цитата
Andrei2016 написал:
Suntor, так я привел его в своем первом сообщении.
Это не код... это его схема с комментариями... так ошибку не поймать.

Эту функцию вашу, где происходит глюк в математике, вынесите в отдельный .lua файл... отрежьте всё лишнее, чтобы остались только те несколько строчек с вычислениями где глючит... но код должен быть рабочий со значениями констант, чтобы его можно было запустить и воспроизвести эффект... после этого его сюда киньте, и мы посмотрим в отладчиках и по коду, что там реально происходит... чтобы найти причину
Цитата
Andrei2016 написал:
Решение оказалось не менее странным, чем сам эффект:
Перед третьим присваиванием "x = (y-z)*param" нужно добавить еще одно присваивание, скажем так:
"x=10". Все - на этом эффект числа с плавающей точкой в x исчезает, и message выводит нормальное 2.
А теперь, если вы уберете "подпорку", эффект double возвращается на место.
Никогда так не делайте... вы закрыли непонятную ошибку, непонятной заплаткой... проблема осталась и выплывет в будущем в самый неподходящий момент. В результате, можете просто деньги потерять от заглючившего скрипта...
 
Suntor, и вы туда же.
Мне что: больше делать нечего, кроме как моделировать странный эффект?
Я вам привел функцию, можете оставить в ней вообще 5 строк и воспроизводить хоть миллион раз. Только, прежде чем соберетесь увидеть этот эффект, увеличьте объем своего программного кода до 200К (без учета объема комментариев) - неважно чем. Вот, тогда и увидите. Это отдельно стоящий, ни с чем несвязанный (ни с какими-либо другими функциями, данными) участок кода. Можете считать его полностью локализованным.
Действие "подпорки" в виде "x=10" однозначно говорит о наличии погрешности при заполнении структуры, отвечающей за идентификацию переменной в Lua. Вопрос в том, из-за чего происходит эта погрешность. Один из возможных вариантов: фоновый вызов сборщика мусора с недостаточной для имеющихся условий частотой. Почему, собственно я и просил разработчиков указать заложенную в текущей реализации терминала частоту вызова сборщика мусора при работающем пользовательском скрипте.
 
Цитата
Andrei2016 написал:
Suntor, и вы туда же.
Мне что: больше делать нечего, кроме как моделировать странный эффект?
Я вам привел функцию, можете оставить в ней вообще 5 строк и воспроизводить хоть миллион раз. Только, прежде чем соберетесь увидеть этот эффект, увеличьте объем своего программного кода до 200К
Сделал .lua файл с таким содержанием:
Код
local function func()
    local x, y, z, param
    y = 10
    z = 5
    param = 2
    x = (y-z)*param
    message("x = "..x)
    x = (y-z)*param
    message("x = "..x)
    x = (y-z)*param
    message("x = "..x)
end

func()
Запустил у себя и получил чётко три сообщения с текстом:
x = 10
x = 10
x = 10
Ничего удивительно.

Теперь, можете скопировать эту функцию (НЕ МЕНЯЯ В НЕЙ НИ ОДНОЙ СТРОЧКИ КОДА!) внутрь своего «большого» 200К скрипта, и посмотреть вывод сообщений.
Я уже знаю в общем-то что у вас получится, но нужно, чтобы вы сами в этом убедились...
 
Цитата
Andrei2016 написал:
x = (y-z)*param  
-- результат в x оказывается числом с плавающей точкой (double). Ошибка в расчетах!
Приведите, пожалуйста, конкретные значения y, z и param, которые дают x - double.
Где-то выше обсуждалось, что 1.9999.... <> 2, но это здесь ни при чем, т.к. в вашем "целочисленном" умножении невозможно получить x=2 (кроме как в 2*1, но здесь не бывает ошибки)

Кроме того,
Цитата
Andrei2016 написал:
пишу я, скажем, x = 3. Да, по факту оно хранится как double (допустим, как 3,000000000001)
НЕТ! По факту оно хранится как 01000000 00001000 00000000 00000000 00000000 00000000 00000000 00000000, т.е ровно 3, и никак иначе! Любые целые числа (в допустимом диапазоне) представляется в double с абсолютной точностью.

НО, учитывайте, что:
1. При сравнении двух double чисел последние 16 разрядов мантиссы игнорируются, что соответствует 12-13 значащим цифрам в десятичном представлении числа.
2. Перед сложением (вычитанием) двух double чисел их мантиссы выравниваются по экспоненте (порядку), в результате вы получаете ошибку в последнем разряде. У чисел ~2 и ~5 уже экспонента отличается на 1 разряд, т.е при их сложении (вычитании) теоретически может возникнуть ошибка в последнем разряде мантиссы. И ошибка ГАРАНТИРОВАННО возникает, если в мантиссе меньшего из складываемых чисел последние два разряда - не нулевые.
На самом деле, при сложении исходно целых чисел ошибка не возникает, а вот при вычитании ошибка ИНОГДА возникает: вы получаете результат на 1 разряд мантиссы меньше, чем должно быть.
Т.е., например, вместо 100 (01000000 01011001 00000000 00000000 00000000 00000000 00000000 00000000) Вы можете получить ближайшее меньшее в double арифметике число 99.999999999999985789145284798..... (01000000 01011000 11111111 11111111 11111111 11111111 11111111 11111111). Это все еще 100 с "запасом" в 15 разрядов.
При умножении все то же самое. Сделайте подряд более 16 действий с "целыми" double числами и ошибка может накопиться до 16 разряда мантиссы. Результат: вы получаете "нецелое" число в результате.
 
Цитата
Алексей написал:
1. При сравнении двух double чисел последние 16 разрядов мантиссы игнорируются, что соответствует 12-13 значащим цифрам в десятичном представлении числа.
Осмелюсь предположить, что вы ошибаетесь.  Если мы говорим об архитектуре x86, то раньше, когде не было еще всяких MMX/SSE, в процессоре были только 80-битные регистры, в которых производились вычисления как double, так и long double.  И вот для double последние 16 бит игнорировались, чтобы сравнивались только 64 бита.  В QLua вроде все работает, как ожидается:
Код
qlua> 1.0000000000000003 == 1.0000000000000004
false
qlua> 1.0000000000000003 ~= 1.0000000000000004
true
qlua> 1.0000000000000003 < 1.0000000000000004 
true
qlua> 1.0000000000000003 > 1.0000000000000004
false
Или я не прав?


Цитата
На самом деле, при сложении исходно целых чисел ошибка не возникает, а вот при вычитании ошибка ИНОГДА возникает: вы получаете результат на 1 разряд мантиссы меньше, чем должно быть.
А пример такой есть?  Я могу представить ситуацию, если магнитуды чисел существенно отличаются.  Но в общем случае не припомню чего-то подобного (но я и не претендую на глубокое понимание вопроса, мог и не знать).  Пример, или ссылку на почитать, плиз :).


В любом случае, проблему OP вы не адресуете, он уверен, что на входе целые числа (правда какие - не говорит, код - не показывает).  Гипотеза - сборщик мусора подворовывает из результатов вычислений, я так понял (опять же, нам не объясняют как, предлагают просто поверить).  Я бы еще мог предположить, что подключается какой-то дикий модуль на C, который плугом вспахивает всю память, но тогда бы все просто взорвалось, а вот вместо 2 получить 1.999999999991 - это почерк прфессионала.  Suntor написал пример, да вот беда: результат первого вычисления x = 10 - будет "подпоркой" для второго (при x=10 все работает - доказано!), результат второго опять x = 10 - будет "подпоркой" для третьего, и сборщик мусора ничего не украдет :(.  Тут нужно глубже копать, читать Иерусалимски, разбираться, откуда растет утверждение
Цитата
Andrei2016 написал:
Была бы в Lua четкая типизация данных, такого эффекта не могло бы быть в принципе.
Жаль, что все идет в виде union.
Ну и радоваться, что нам достались версии Quik безо всех этих проблем :).
 
Цитата
Алексей написал:
1. При сравнении двух double чисел последние 16 разрядов мантиссы игнорируются, что соответствует 12-13 значащим цифрам в десятичном представлении числа.
Первый раз такое слышу... это вообще откуда такое?... можно ссылочку.
Подозреваю, что вы перепутали 64-хбитный double и внутрисопроцессорный 80-ибитный long double. Эти 16 разрядов отбрасываются не от double'а, а от вычисленного внутри сопроцессора long double при возврате обратно из сопроцессора и привидении его к типу double, то-есть 80-16=64. Точность double при сравнении не меняется, там около 17 десятичных цифр получается.

Такой вот простой .lua файл:
Код
local     DBL_EPSILON = 2.2204460492503131e-016
local NOT_DBL_EPSILON = DBL_EPSILON/2

local function checkDblEpsilon(v1, v2)
    message(string.format("(%.20g == %.20g) == ", v1, v2)..(v1 == v2 and "true" or "false"))
end

checkDblEpsilon(1.0, 1.0+NOT_DBL_EPSILON)
checkDblEpsilon(1.0, 1.0+DBL_EPSILON)
Выдаёт:
Код
(1 == 1) == true
(1 == 1.0000000000000002) == false
Как и положено. Никакие последние разряды не откидываются.
Страницы: 1
Читают тему
Наверх