Попытка победить floating-point
| Правила | Регистрация | Пользователи | Сообщения за день |  Справка по форуму | Файлообменник |

Вернуться   Форум DWG.RU > Программное обеспечение > Программирование > LISP > Попытка победить floating-point

Попытка победить floating-point

Ответ
Поиск в этой теме
Непрочитано 13.10.2025, 02:44 #1
Попытка победить floating-point
Browning Zed
 
Регистрация: 17.01.2014
Сообщений: 100

День добрый.
Задача написать функцию которая подсчитывает, и возвращает количество знаков после точки. В качестве аргумента функция принимает число (integer или real). Возвращаемое значение - integer.
Если передаваемое, в качестве аргумента, число - это integer, либо real с нулевой дробной частью, функция возвращает 0, в противном случае - количество знаков после точки.
Среда выполнения AutoCAD (BricsCAD или иные CAD-системы поддерживающие AutoLisp могут иначе обрабатывать double precision).
Пусть функция называется DecimalPlaceNum. Ожидаемый return функции:
Код:
[Выделить все]
 
(DecimalPlaceNum 8) ; возвращает 0
(DecimalPlaceNum 12.00) ; возвращает 0
(DecimalPlaceNum 4.65) ; возвращает 2
(DecimalPlaceNum 1.9742) ; возвращает 4
Создать такую функцию особых проблем не вызывает, и в большинстве сценариев она отработает корректно. Но некоторые передаваемые, в качестве аргумента, числа заведут вычисления в глухой лес. В качестве примера такого "вредного" числа возьмем 1.13
Код:
[Выделить все]
 ;; Попытка победить № 1. Итеративно
(defun DecimalPlaceNum ( num / n )
	(setq n 1)
	(while
		(not (equal 0. (rem (* num n) 1) 1e-6))
		(setq n (* 10 n))
	)
	(1- (strlen (itoa n)))
)
;; выражение (DecimalPlaceNum 1.13) вернет 3, а должно 2
Код:
[Выделить все]
 ;; Попытка победить № 2. Рекурсивно
(defun DecimalPlaceNum ( num )
	(if (> (abs (rem num 1)) 1e-6)
		(1+ (DecimalPlaceNum (* num 10.)))
		0
	)
)
;; выражение (DecimalPlaceNum 1.13) вернет 16, а должно 2
Код:
[Выделить все]
 ;; Попытка победить № 3. Костыль
(defun DecimalPlaceNum ( num / str pos )
	(setq str (vl-string-right-trim "0" (rtos num 2 14)))
	(if (setq pos (vl-string-position (ascii ".") str))
		(- (strlen str) pos 1)
		0
	)
)
;; тут функция отработает корректно - выражение (DecimalPlaceNum 1.13) вернет 2, как и должно
Получается, из всех вариантов рабочий только под номером 3. Но имхо, это костыль, в том плане, что задача, в которой операндами выступают числовые данные, решается исключительно через string-функции. Выходит "некостыльных" вариантов решения этой задачи не существует?

Последний раз редактировалось Browning Zed, 13.10.2025 в 15:53.
Просмотров: 552
 
Непрочитано 13.10.2025, 07:49
#2
Кулик Алексей aka kpblc
Moderator


 
Регистрация: 25.08.2003
С.-Петербург
Сообщений: 40,490


Ты пост-то смотрел прежде чем отправить? )) Глаза же сломать можно )
Глянь https://autolisp.ru/2021/05/19/chto-...-te-rezultaty/ - это к вопросу об использовании rtos.
И я б не сильно доверял любому из вариантов. Потому как то же самое умножение на 10 вполне может увеличивать ошибку. Казалось бы, простейший код типа
Код:
[Выделить все]
 (defun get-dec-place-num (value / subval len)
  (cond
    ((= (type value) 'int) 0)
    ((= (type value) 'real)
     (setq value  (abs value)
           subval (* (- value (fix value)) 10)
           len    0
     ) ;_ end of setq
     (while (/= (fix subval) subval)
       (setq len    (1+ len)
             subval (* subval 10)
       ) ;_ end of setq
     ) ;_ end of while
     len
    )
  ) ;_ end of cond
) ;_ end of defun
На числе 12,345 может выдать значение 16: только потому, что в какой-то момент (fix subval) становится 345, а разница между subval и (fix subval) - 6.25e-13. Ну и естественно, что код не останавливается.
__________________
Моя библиотека lisp-функций
---
Обращение ко мне - на "ты".
Все, что сказано - личное мнение.
Кулик Алексей aka kpblc вне форума  
 
Непрочитано 13.10.2025, 12:16
#3
avatarez


 
Регистрация: 08.08.2019
Сообщений: 22


Почему же 3 вариант - костыль? Число все равно в памяти хранится как двоичное: 1 бит для знака, 11 бит для экспоненты и 52 бита для мантиссы. Так что для самой машины десятичное число это уже костыль, существующий для удобства представления пользователю.

А вычитание типов double - вообще отдельная тема https://ru.stackoverflow.com/questions/1073688
avatarez вне форума  
 
Автор темы   Непрочитано 13.10.2025, 15:15
#4
Browning Zed


 
Регистрация: 17.01.2014
Сообщений: 100


Цитата:
Сообщение от Кулик Алексей aka kpblc Посмотреть сообщение
Ты пост-то смотрел прежде чем отправить? )) Глаза же сломать можно )
Форматирование поправил. Поторопился пост отправить, не проверил.
Цитата:
Сообщение от Кулик Алексей aka kpblc Посмотреть сообщение
Потому как то же самое умножение на 10 вполне может увеличивать ошибку.
Да, знаю этот прикол. С каждой новой итерацией, умножение числа будет накапливать ошибку.
Цитата:
Сообщение от Кулик Алексей aka kpblc Посмотреть сообщение
На числе 12,345 может выдать значение 16: только потому, что в какой-то момент (fix subval) становится 345, а разница между subval и (fix subval) - 6.25e-13. Ну и естественно, что код не останавливается.
Ну, вроде потестил свой последний вариант с рядом чисел, которые могли бы закосячить результат. Пока проблем не выявил.
Цитата:
Сообщение от avatarez Посмотреть сообщение
Почему же 3 вариант - костыль?
Думаю, классическим подходом к решению подобной задачи, было бы оперирование остатком деления числа. А тут, получается, мы конвертируем числовой тип в строковый, чтобы найти где в строке расположена точка. Но может, и правда, такой подход тоже имеет смысл. И то, что я рассматриваю подобный вариант, как костыль, это мои тараканы в голове.
Browning Zed вне форума  
 
Непрочитано 14.10.2025, 13:08
#5
===AAA===


 
Регистрация: 15.08.2005
г. Норильск
Сообщений: 628


А какая точность нужна?
Для числа pi какой ответ ожидается?
__________________
Счастливо, Алексей!
===AAA=== вне форума  
 
Непрочитано 14.10.2025, 14:43
1 | #6
Сергей812


 
Регистрация: 10.08.2013
Сообщений: 11,509


Цитата:
Сообщение от Browning Zed Посмотреть сообщение
А тут, получается, мы конвертируем числовой тип в строковый, чтобы найти где в строке расположена точка. Но может, и правда, такой подход тоже имеет смысл. И то, что я рассматриваю подобный вариант, как костыль,
оформите в виде отдельной функции, потом уже в готовой программе на реальных наборах данных можете экспериментировать с производительностью/используемыми ресурсами. Внутри лисповских строковых функций скорее всего быстрые сишные функции вызываются, поэтому спорный вопрос по производительности - реализация циклов на лиспе или вызов пару строковых функций лиспа.
Сергей812 вне форума  
 
Автор темы   Непрочитано 14.10.2025, 17:11
#7
Browning Zed


 
Регистрация: 17.01.2014
Сообщений: 100


Цитата:
Сообщение от ===AAA=== Посмотреть сообщение
А какая точность нужна?
Для числа pi какой ответ ожидается?
Как я писал выше, если передаваемое, в качестве аргумента, число - это integer, либо real с нулевой дробной частью, функция возвращает 0, в противном случае - количество знаков после точки.
В случае бесконечных десятичных дробей, вроде числа pi, возвращаемый результат функции, думаю, должен быть в диапазоне от 15 до 17. То есть по стандарту IEEE 754, в соответствии с которым, числа двойной точности хранятся в памяти AutoCAD. В этом отношении, показательным примером будет функция под номером 2 из первого поста темы:
Код:
[Выделить все]
 (defun DecimalPlaceNum ( num )
	(if (> (abs (rem num 1)) 1e-6)
		(1+ (DecimalPlaceNum (* num 10.)))
		0
	)
)
(DecimalPlaceNum pi) вернет 15
(DecimalPlaceNum (/ 1.0 3)) вернет 17
Впрочем, вполне возможно, было бы ограничиться числом 15, как фиксированной величиной. Более высокая точность, наверное, мало где применима в рамках CAD. Но в связи с тем, что начиная именно с 15-го разряда и выше "зашумление погрешностью" становится наиболее выраженно, приходится ограничиваться 14-тью значащими десятичными цифрами, что приводит к третьему варианту функции из первого поста: (rtos num 2 14) - это вынужденный подход, за неимением иных вариантов решения задачи.

Цитата:
Сообщение от Сергей812 Посмотреть сообщение
оформите в виде отдельной функции, потом уже в готовой программе на реальных наборах данных можете экспериментировать с производительностью/используемыми ресурсами.
Да, думаю, так и сделаю.

Последний раз редактировалось Browning Zed, 14.10.2025 в 18:40.
Browning Zed вне форума  
Ответ
Вернуться   Форум DWG.RU > Программное обеспечение > Программирование > LISP > Попытка победить floating-point



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Point Name, 3D offset и др. в Autodesk Land Desktop 2007 Re-Maker Вертикальные решения на базе AutoCAD 2 12.08.2014 10:19
C#.NET. Переопределение ручек (Grip Overrule). Do$ .NET 7 20.05.2013 15:22
AutoCAD2010 выдает 82 отчета об ошибке за раз Nusia AutoCAD 22 29.08.2012 16:11
Не работает Temporary Track Point Stelth AutoCAD 2 25.01.2011 15:09
Regen Holon Программирование 28 03.08.2007 15:18