Сравнение вещественных чисел.

Страницы: 1
RSS
Сравнение вещественных чисел., (55.3 < 55.3) - верно!
 

Здравствуйте.

При сравнении двух вещественный чисел скрипт дал неверный результат:

Первое число x1 – получено из свечи графика (.high) второе вычислено.

Оба числа одинаковые 55.3, однако при сравнении срабатывает ветка else.

if (x1<=x2) then

               ...

else

...

end

Так же, неправильно работают варианты:

if (x1>x2) then  (условие срабатывает, хотя числа равны)

if (x1*1<=x2*1) then  (Условие не срабатывает)

Вариант с целыми числами  if (x1*10<=x2*10) – работает корректно.

В большинстве случаев такой проблемы не наблюдается.

Если x1 и x2 задать самостоятельно local x1=55.3 и local x2=55.3 то логика работает корректно.

Что это за ошибка и как от нее обезопаситься? Все переводить в целые и работать в целых?

 
Сергей, Скорее всего, никакой ошибки нет, поскольку на самом деле числа там что-то вроде 55.30001 и 55.29999 или что-то в этом роде. а вот "переводить в целые и работать в целых" не получится - никаких целых в этом языке просто нет.  :smile:  
 
Цитата
Владимир написал:
Сергей, Скорее всего, никакой ошибки нет, поскольку на самом деле числа там что-то вроде 55.30001 и 55.29999 или что-то в этом роде. а вот "переводить в целые и работать в целых" не получится - никаких целых в этом языке просто нет.  ::  
Тогда, что значит "на самом деле" ? В лог файле они именно как 55.3 пишутся, без всяких хвостов и "-e".
И если нет никаких целых, то почему два новых числа, полученные путем умножения старых на 10, сравниваются иначе?
 
Сергей, А КАК ИМЕННО они "в лог пишутся"? Может, "%1.1f"? Код приведите...
 
Цитата
Владимир написал:
Сергей, А КАК ИМЕННО они "в лог пишутся"? Может, "%1.1f"? Код приведите...
save_log("x1="..x1)
Код
function save_log(st)
   local path=getScriptPath().."\\"..options.script_name..".log"
   local fn=io.open(path, "a") 
   fn:write(st.."\n")
   fn:close()
end
 
При сравнении чисел с сотыми, типа 55.31 - все работает хорошо. Может быть это как-то связано с тем, что у фьючерса шаг цены 0.01... Только как?
 
Сергей, Да плевать на все "шаги фьючерса"! Попробуйте вывести в ветке else что-то типа:
F:write(string.format("x1=%1.5f x2=%1.5 x1*10=%1.5f x2*10=%1.5f\n",x1,x2,x1*10,x2*10));
 
Еще одна интересная деталь, если число, которое я вычисляю, задать явно: local x2=55.3 то всё ок.
А вычисляю я его округлением:
Код
x= math.ceil(price/options.price_step) * options.price_step
где options.price_step=0.01
 
Владимир, О, Господи! Вы что, на каждый чих лог открываете?! Его надо открывать ОДИН раз в начале работы скрипта и закрывать в конце.
 
Цитата
Владимир написал:
string.format
x1=55.30000 x2=55.30000 x1*10=553.00000 x2*10=553.00000
Равенство не срабатывает. Но если x2 полученное из math.ceil прописать явно, то работает.
 
Попробовал найти разницу между x1 и x2, вот результат: x=-7.105427357601e-15
А вот, как выглядят вещественные числа в 17 знаками после запятой: x1=55.30000000000000426 x2=55.29999999999999716 x1*10=553.00000000000000000 x2*10=553.00000000000000000
В общем, проблема понятна...
В качестве решения, первое что приходит в голову: использовать условия только "больше"/"меньше" без равенств, и добавить некую погрешность(меньше шага цены)
local pogr=0.001
if (x1>x2+pogr) then ...
 
Ну да, стандартное решение в подобных случаях - добавить некий "эпсилон", очень малый по сравнению с "рабочими" числами"
 
Обычно, все же, применяют округление к итоговому результату.
У Вас math.ceil(price/options.price_step) * options.price_step есть результат работы с вещественными числами. Его и надо привести к нужной точности, а потом уже сравнивать.
 
Что то мне подсказывает, что с типизацией могут быть проблемы.
Попробуйте
Код
if(tonumber(x1)>tonumber(x2))then
else
end 
 
Цитата
Алексей написал:
tonumber
tonumber - не решил проблему.
 
Возьмите типовую функцию округления
Код
---@param num number
---@param idp any
local function round(num, idp)
    if num then
        local mult = 10^(idp or 0)
        if num >= 0 then
            return math.floor(num * mult + 0.5) / mult
        else
            return math.ceil(num * mult - 0.5) / mult
        end
    else
        return num
    end
end

И используйте

round(round(price/options.price_step, scale) * options.price_step, scale) > price1
 
Цитата
Nikolay написал:
local function round(num, idp)
   if num then
       local mult = 10^(idp or 0)
       if num >= 0 then
           return math.floor(num * mult + 0.5) / mult
       else
           return math.ceil(num * mult - 0.5) / mult
       end
   else
       return num
   end
end
Тоже самое, при вызове  round(55.3, -2) в результате мусор в районе 15-го знака после запятой. Я так понимаю, этот мусор "не лечится", исходя из самих принципов хранения вещественных чисел. Поэтому сейчас в код добавил некую погрешность pogr, которая меньше шага цены, но больше возможного "мусора". if (x1+pogr>x2) then ,
В итоге (55.3+0.00001>=55.3) -> верно.
 
Ну раз ничего логичного не помогает, то открою тебе особое секретное колдунство древних монахов lua

Для параметра который у тебя вычисляется после вычисления примени вот такой хак
Код
x= math.ceil(price/options.price_step) * options.price_step
x=tonumber(tostring(x))
 
Ну да, в динамически типизированных языках, можно после округления привести в строку, обрезать до нужной длины, а потом обратно в число.
 
Цитата
Алексей написал:
x=tonumber(tostring(x))
Это магия! ))))
 
Сергей, Эпсилон работает не в пример быстрее и не в пример надёжнее. :smile:  
 
А зачем при вызове round(55.3, -2) параметр округления -2, если он 2.
 
Стандартные эпсилоны

define(`m4_flt_epsilon', `m4_calc(2**-23)') # Машинный эпсилон float (32 бита)
define(`m4_dbl_epsilon', `m4_calc(2**-52)') # Машинный эпсилон double (64 бита)
define(`m4_ldbl_epsilon', `m4_calc(2**-63)') # Машинный эпсилон long double (80 бит)
www.bot4sale.ru        t.me/bot4sale
 
Люди новые, вопросы десятилетиями одни и те же...

https://forum.quik.ru/forum10/topic2229/
https://forum.quik.ru/forum10/topic1881/
https://forum.quik.ru/forum10/topic2572/

Почитайте. Там и ссылки на разные растолковывающие материалы есть.
 
Цитата
Сергей написал:
Попробовал найти разницу между x1 и x2, вот результат: x=-7.105427357601e-15
А вот, как выглядят вещественные числа в 17 знаками после запятой: x1=55.30000000000000426 x2=55.29999999999999716 x1*10=553.00000000000000000 x2*10=553.00000000000000000
В общем, проблема понятна...
В качестве решения, первое что приходит в голову: использовать условия только "больше"/"меньше" без равенств, и добавить некую погрешность(меньше шага цены)
local pogr=0.001
if (x1>x2+pogr) then ...
Кстати да тоже такое нашел как то, много думал )), теперь понятно откуда ноги растут
Страницы: 1
Читают тему (гостей: 1)
Наверх