Author Topic: Строки vs Числа  (Read 8110 times)

0 Members and 1 Guest are viewing this topic.

GHksd5sf3sQdewrj

  • Зашел в гости
  • *
  • Posts: 6
    • View Profile
Строки vs Числа
« on: May 10, 2014, 11:25:39 AM »
Не смог найти полноценную функцию преобразования строк в числа (применительно к десятичным дробям). Если она есть, то может кто-нибудь подскажет?
А так как я её не нашёл, то реализовал скриптик такого преобразования на основе int (). Правда, получился не совсем скриптик, а некий исследовательский скрипт с кучей комментариев, из которых, я думаю, всё понятно. Поэтому здесь описывать не буду - смотрите скрипт и комментарии в нём. Проблема вылезла при попытке сравнения строки ввода с числами. Там оказалось довольно много для меня интересного. Не судите строго, для корифеев всё это, возможно, азбука. Но я недавно освоение кликера начал.
Code: [Select]
// Перевод строки в число
// Author: GHksd5sf3sQdewrj
// Version: 1.0 (10.05.2014)

#name "txt2num"

// Данный скрипт тестовый. Поводом к его написанию послужило то, что я не смог
// найти нормального способа преобразования строки, содержащей число в виде
// десятичной дроби в собственно число. Целые числа можно преобразовывать
// с помощью функции int (). Для десятичных дробей она не работает.
// Точнее, работает криво, заодно округляя число до целого.
// Скрипт пытается сравнивать введенное число (в текстовом виде) с 555.55
// Поэкспериментируйте с разыми числами и вы увидите, что результат сравнения
// зависит не от числа, а от количества символов в нём и ещё чего-то.
// Например, попробуйте ввести числа 675.1, 987.4, 338.567, 222.121 и т.п.
// При попытке сравнения чисел, содержащих столько же символов, сколько и
// тестовое число 555.55 (например, "1.2456", "236.22", "987445", "24.568")
// обнаружен дополнительный глюк. В блоке вывода есть комментарий.
// Реализован некий защитный функционал, анализирующий правильность ввода
// (собственно, я его не писал для решения этой простой задачи, а просто
// скопировал из другого скрипта). Некоторые символы в строке ввода
// принципиально не допустимы, например "/". При их наличии в результирующей
// переменной скрипт просто "вылетает" с ошибкой при попытке что-то сделать
// с этой переменной.
// Отрицательные числа я не рассматривал, введение "-" скрипт воспринимает, как
// ошибку. Мне они не нужны были. У кого нужда возникнет, думаю, допилит.
// Для остановки скрипта воспользуйтесь сочетанием клавиш Alt+S (по дефолту)
// или введите вместо числа слово "stop".
define ($a) // число, как строка
define ($b) // число, как число
define ($i)
define ($jj)
define ($pos) // позиция точки
define ($la)
define ($ra)
define ($r)
define ($SimbolFound)
define ($NumPoint)
For ($i, $i < 10)
   $sim [$i] = $i // Что интересно, вроде числа по массиву разгоняю, а далее
   // использую их для сравнения со строками. Вполне работает. Впрочем,
   // далее будет видно, что простые сравнения чисел и строк вполне проходят.
   // Возможно ещё оказывает какое-то влияние, что ниже 2 элемента массива
   // строковые. Интересно, как кликер в теории относится к смешанным элементам в массиве?
End_cyc
$sim [10] = "."
$sim [11] = ","
LOGSHOW (1)
If (WNDFIND ("Clickermann - Лог") > 0)
   WNDSIZE (WNDFIND ("Clickermann - Лог"), 720, 350)
End_if
print ("-------------------------------------------------------------")
Print ("Для остановки скрипта воспользуйтесь сочетанием клавиш Alt+S")
Print (" (по дефолту) или введите вместо числа слово stop.")
print ("")
print ("Скрипт пытается сравнивать введенное число (в текстовом виде) с 555.55")
print ("Поэкспериментируйте с разыми числами и вы увидите, что результат")
print ("сравнения зависит не от числа, а от количества символов в нём.")
print ("Например, попробуйте ввести числа 675.1, 987.4, 338.567, 222.121 и т.п.")
next:
$a = INPUTBOX("Введите десятичное число (предпочтительно с дробной частью). Для остановки скрипта введите слово stop.", "", 3600)
if (($a = "stop") | ($a = "стоп") | ($a = "STOP") | ($a = "Stop"))
   halt
end_if
// ### Начало блока преобразователя строки $a в число $b ###
// - для вставки в другие скрипты. Тут же проверялка против неверного ввода.
$pos = 0
$NumPoint = 0
$SimbolFound = 0
for ($i = 1, $i < STRLEN ($a) + 1)
   For ($jj, $jj < 12)
      If (STRCUT ($a, $i, 1) = $sim [$jj])
         $SimbolFound = 1
      End_if
   End_cyc
   If ($SimbolFound = 0)
      print ("")
      print ("Введенное выражение содержит недопустимые символы!")
      goto (next)
   End_if
   if ((STRCUT ($a, $i, 1) = ".") or (STRCUT ($a, $i, 1) = ","))
      $NumPoint = $NumPoint + 1
      $pos = $i
   end_if
end_cyc
if ($NumPoint > 1)
   print ("")
   print ("Введенное выражение содержит более одной десятичной точки (запятой)!")
   goto (next)
end_if
if ($pos = 0)
   $pos = STRLEN ($a) + 1 // Для случая введения целого числа без точки
end_if
$la = STRCUT2 ($a, 1, $pos - 1) // целая часть числа (строка)
$ra = strcut2 ($a, $pos + 1, STRLEN ($a)) // дробная часть числа (строка)
$r = strlen ($a) - $pos // разрядность дроби (не верно для целого числа, но работает за счёт нулевой дробной части)
$b = int ($la) + int ($ra) / pow (10, $r) // Собираем число
// ### Конец блока преобразователя ###
// Блок вывода результатов
print ("")
// Прошу обратить внимание, что тут я сравниваю строковую переменную $a с числом.
// В $a содержится без изменений то, что Вы ввели в диалоговом окошке.
if ($a > 555.55)
   print ("Ваше число ", $a, " больше 555.55 Верно?")
end_if
if ($a < 555.55)
   print ("Ваше число ", $a, " меньше 555.55 Верно?")
end_if
// По результатам будет очевидно, что сравнение происходит по правилу сравнения строк -
// сравнивается длина строки $a с "длиной" числа 555.55
if ($a = 555.55)
   print ("Ваше число ", $a, " равно 555.55 Верно?")
   // А это условие на предмет равенства вообще прикольно работает :)
   // Если число символов в строке $a совпадает с числом цифр (плюс точка),
   // короче, с числом символов в числе 555.55, то условие равенства
   // выполняется исключительно для строки "555.55". Таким образом,
   // для строк типа "1.2456", "236.22", "987445", "24.568", "555,55" (с запятой) не выполняется
   // ни одно из трёх условий выше и в лог не выводится ни одно из 3-х
   // сообщений. Для этого случая выведем следующее:
end_if
if ((STRLEN ($a) = STRLEN ("555.55")) & ($a ! "555.55"))
   print ("Исключительный случай. При длине строки равном числу символов")
   print ("в числе 555.55 сравнение не проходит (кроме самого 555.55). См. комментарии")
end_if
if ($a = $b)
   Print ("Строковое $a ", $a," вполне равно числому $b ", $b, ". Но бывают исключения.")
   // Данный результат получается в большинстве стандартных ситуаций, например,
   // для чисел 123.546, 555.55, 124.78, 1.2, 689, 1002, 7896.455,
   // Но он не будет получен для очень больших чисел с дробной частью,
   // например, 145689875324.234, при вводе пустой строки "" не равно 0,
   // при вводе неправильных чисел, начинающихся или заканчивающихся точкой
   // (запятой), например, .123 4567. 455. .1456 (может это и не правильно, но мой
   // скрипт способен преобразовывать эти конструкции в целые числа и числа вида
   // 0.ххх соответственно)
   // Так же, когда число было введено через запятую - но это уже моё извращение
   // исключительно ради удобства. Числа в Clickermann пишутся через точку (.).
   // Для этих обнаруженных случаев (может есть и ещё) будет выдано сообщение ниже
else
   Print ("Строковое $a ", $a," не равно числовому $b ", $b," (иногда появляется")
   print ("В скрипте есть комментарий по обнаруженным случаям таких сообщений.")
End_if
goto (next)
halt // for single run
« Last Edit: May 10, 2014, 12:52:42 PM by GHksd5sf3sQdewrj »

Vint

  • Супермодератор
  • Герой форума
  • *
  • Posts: 3935
  • Лечу куда хочу. cman 4.13.014x32, 4.14.003 W10
    • View Profile
Re: Строки vs Числа
« Reply #1 on: May 10, 2014, 12:55:58 PM »
Сразу скажу, скрипт пока не смотрел.
А не проще сделать всё наоборот. Число перевести в строку и сравнить строки? Конечно это пройдет только если нужно равно/не равно.   В случае больше/меньше такое не прокатит.


GHksd5sf3sQdewrj

  • Зашел в гости
  • *
  • Posts: 6
    • View Profile
Re: Строки vs Числа
« Reply #2 on: May 10, 2014, 01:11:42 PM »
Нужно больше-меньше. Кроме того, нужна арифметика с введенными данными. А она тоже не работает. К строке тупо присоединяет в конец всё, что пытаешься прибавить, примножить вместе со знаками. В принципе, меня устраивает и моя реализация. Тем более возникла потребность ввести 2 числа в самом начале работы, надоело скрипт перед каждым запуском править. О постоянном диалоге с работающим скриптом речи пока нет. Но мне кажется, нормальная функция преобразования строки в число (с сохранением дробной части) всё же необходима. Или подумать над системой ввода данных. А то вводится только строка и трава не расти.

Только что нашел в справке: половину моей проверялки на валидность символов можно реализовать через strfilter ($a, "0123456789.,", 1) и сравнение результата с исходным $a. Но останется нереализованным подсчёт точек-запятых. Опять же явно не хватает функции подсчёта числа вхождений подстроки в строку. Есть интересная STRPOS ("str", "substr") - числовая функция; возвращает позицию подстроки в строке. Но подозреваю, что она вернёт только первое вхождение, а прочие, скорее всего, просто проигнорирует. Можно через неё реализовать подсчёт запятых, отбрасывая найденный кусок и продолжая поиск в оставшейся части, но это уже варианты.
« Last Edit: May 10, 2014, 01:32:50 PM by GHksd5sf3sQdewrj »

Atas

  • Активный участник
  • ***
  • Posts: 147
    • View Profile
Re: Строки vs Числа
« Reply #3 on: May 10, 2014, 01:20:39 PM »
Может я не понял поставленной задачи, но для преобразования строки в число подходит функция ROUND (arg, prec).

[spoiler]Синтаксис
ROUND (arg, prec) - числовая функция; округляет число до заданной точности


Параметры
arg - число
prec - точность


Пример

$r = ROUND(1236, 1) // $r = 1240
$r = ROUND(1236, 2) // $r = 1200

$r = ROUND(1.236, -2) // $r = 1.24
$r = ROUND(1.236, -1) // $r = 1.2
$r = ROUND(1.236, 0)  // $r = 1



Примечания
Округление происходит в сторону от нуля. То есть prec = 2 округлит число до сотен, а prec = -2 до сотых (два знака после точки). Откругление происходит по стандартному правилу 0.5
[/spoiler]

GHksd5sf3sQdewrj

  • Зашел в гости
  • *
  • Posts: 6
    • View Profile
Re: Строки vs Числа
« Reply #4 on: May 10, 2014, 01:55:19 PM »
Может я не понял поставленной задачи, но для преобразования строки в число подходит функция ROUND (arg, prec).


Ага. То, что надо. Вот только интересно, как об этом предполагалось догадываться. )) В справке, которую ты процитировал, о строках вроде не упомянуто...
Видимо, аналогию с int () надо было провести. ) Правда, для числа с произвольным количеством знаков после запятой нужно ещё найти эту точность округления, если надо чего-нибудь не потерять, но это решаемо опять же анализом строки, и мне это не надо - мне 3 знака после запятой нужны. А 1 или 2 знака до 3-х она замечательно округляет.

Спасибо большое.  ;)
« Last Edit: May 10, 2014, 02:00:46 PM by GHksd5sf3sQdewrj »

Крис Тинка

  • Активный участник
  • ***
  • Posts: 171
    • View Profile
Re: Строки vs Числа
« Reply #5 on: May 10, 2014, 01:59:33 PM »
И тут же найден баг в программе))) Функция ROUND  работает не корректно при округлении до 7 знаков и выше после точки.
Code: (clickermann) [Select]
$b = ROUND (INPUTBOX("Введите число",10,10), -7)
PRINT("b = ", $b)
HALT
Вводим число от -214 до 214 - всё хорошо. Вводим 215 - не правильное вычисление
При округлении до 8 знаков после точки диапазон другой...

GHksd5sf3sQdewrj

  • Зашел в гости
  • *
  • Posts: 6
    • View Profile
Re: Строки vs Числа
« Reply #6 on: May 10, 2014, 02:20:45 PM »
И тут же найден баг в программе))) Функция ROUND  работает не корректно при округлении до 7 знаков и выше после точки.
Code: (clickermann) [Select]
$b = ROUND (INPUTBOX("Введите число",10,10), -7)
PRINT("b = ", $b)
HALT
Вводим число от -214 до 214 - всё хорошо. Вводим 215 - не правильное вычисление
При округлении до 8 знаков после точки диапазон другой...
Не только до 7 знаков. До любого количества знаков, в зависимости от целой части числа. Поставь, например, до 2-х знаков и введи что-нибудь типа 56327743895.223 - получишь  20722644.6600000001 - не только число другое, но ещё и 00000001 выдало при округлении до 2-х знаков. Ведёшь 327743895.223 - получишь -15853488.4600000009 (также до 2-х знаков). При округлении до 11 знаков уже не может числа в районе 1 округлить, но округляет что-то типа 0.00000012356
« Last Edit: May 10, 2014, 02:26:24 PM by GHksd5sf3sQdewrj »