[Решено] LO Calc. Вычисления с дробями. Простой пример, странное поведение.

Автор ost, 1 августа 2022, 14:22

0 Пользователи и 1 гость просматривают эту тему.

ost

Доброго.
Не могу понять причину получения сообщения "Не равно" при запуске макроса "Main" модуля "Module1" библиотеки Library1 файла по ссылке.
Макрос вычисляет сумму значений первых двух ячеек и сравнивает со значением третьей. Поскольку сумма и значение равны ожидал увидеть сообщение "Равно"
Что я делаю не так?

Спасибо.


https://disk.yandex.ru/i/8xv3HO92SYwNyQ

mikekaganski

Увидеть причину Вам поможет такая формула:

=RAWSUBTRACT(A1+A2;A3)

См. этот FAQ. Применимо к практически любым приложениям, работающим с дробными числами (кроме специализированных пакетов наподобие библиотек для вычислений с расширенной точностью).
С уважением,
Михаил Каганский

Bigor

Поддержать разработчиков LibreOffice можно можно тут, а наш форум вот тут

eeigor

#3
Да, так и есть. Ну, я округлил все сравниваемые значения до 2-х знаков после запятой, и всё сработало как надо.
Видимо тип Double добавляет какую-то дробную цифирь в позиции (разряде) "отсюда не видать".

Edit:
У нас, что, нет функции округления в Basic (runtime)?

Function Round(BaseValue as Double, Decimals as Integer) as Double
   Dim Multiplicator as Long
   Dim DblValue#, RoundValue#

   Multiplicator = Power(10,Decimals)
   RoundValue = Int(BaseValue * Multiplicator)
   Round = RoundValue/Multiplicator
End Function


Print aData(2)(0) - aData(0)(0) - aData(1)(0)
Ubuntu 18.04 LTS • LibreOffice 7.3.5.2 Community

sokol92

#4
Вы находитесь в мире приближенных вычислений. В этом мире для дробных чисел нет понятий "=" и "<>" (и округление не является выходом).
Равно:

ABS(x-y)<=EPS

Не равно:

ABS(x-y)>EPS

где EPS - достаточно малое число, которое надо указывать с учетом диапазона значений x и y.

Например, для бухгалтерии предприятия, где суммы указываются с копейками, мы можем взять EPS=1E-4.
Для экзабайтов и целые числа придется сравнивать таким образом, причем EPS уже будет заведомо больше 1.
Владимир.

mikekaganski

#5
Цитата: eeigor от  1 августа 2022, 14:50У нас, что, нет функции округления в Basic (runtime)?

Нет, если не включена совместимость VBA.

Цитата: eeigor от  1 августа 2022, 14:50
Function Round(BaseValue as Double, Decimals as Integer) as Double
   Dim Multiplicator as Long
   Dim DblValue#, RoundValue#

   Multiplicator = Power(10,Decimals)
   RoundValue = Int(BaseValue * Multiplicator)
   Round = RoundValue/Multiplicator
End Function


Этот код создаёт дополнительное огромное искажение за счёт увеличения количества неточных операций с плавающей точкой. Так делать очень не рекомендуется. См. ответ @sokol92.
С уважением,
Михаил Каганский

sokol92

#6
Цитата: eeigor от  1 августа 2022, 14:50нет функции округления в Basic
Легко сделать из подручных средств (только помним, что округление "банковское"):

Option VbaSupport 1
Option Explicit

Function VBA_Round(Byval expression, Optional Byval numdecimalplaces As Long)
 VBA_Round=Round(expression, numdecimalplaces)
End Function


Используем VBA_Round в любых модулях.
Аналогично можно записать и другую полезную функцию VBA_InstrRev.
Владимир.

mikekaganski

#7
Для педантичных.
Пример сравнивает сумму 3340,33 и 3079,78 с числом 6420,11.

В Calc числа представлены в виде 64-разрядного двоичного числа с плавающей точкой стандарта IEEE 754. Эти числа используют 52 бита для мантиссы (с подразумеваемой 1 в нулевом бите, эффективная разрядность мантиссы - 53 бита), 11 бит для порядка (экспоненты), и 1 бит для знака.

Как известно, не всякое число можно записать в виде конечной дроби. Из тех, что можно, не всякое можно записать в виде конечной десятичной дроби. Среди оставшихся не всякое можно уместить в заданное число знаков.

Тем более не всякое число можно записать в виде конечной двоичной дроби, и тем более в виде двоичной дроби с не более чем 53 значимыми цифрами. Все десятичные числа из примера - как раз такие непредставимые в двоичном виде числа.

Число 3340,33 в двоичном виде представлено своим приближённым значением: 0 10000001010 1010000110001010100011110101110000101000111101011100, что в десятичном виде равно 3340,329999999999927240423858165740966796875 (меньше идеального значения на ~0,7*10-13).
Число 3079,78 в двоичном виде также представлено приближённо: 0 10000001010 1000000011111000111101011100001010001111010111000011, что в десятичном виде равно 3079,78000000000020008883439004421234130859375 (больше идеального значения на ~2,0*10-13).
Приближение числа 6420,11 в двоичном виде: 0 10000001011 1001000101000001110000101000111101011100001010001111, что в десятичном виде равно 6420,1099999999996725819073617458343505859375 (меньше идеального значения на ~3,3*10-13).

В таком виде они на самом деле и хранятся в ячейках A1, A2 и A3.

Учитывая сказанное, при сложении A1 и A2 должно бы получиться точно 6420,11000000000012732925824820995330810546875 (на ~1,3*10-13 больше идеала). Но и этот результат не может быть представлен в машинном виде, и округляется до 6420,110000000000582076609134674072265625, которое ещё больше отличается от идеала (на ~5,8*10-13), и которое уже будет сравниваться с A3.

Поставим два числа в двоичном представлении рядом: значение A3 и результат суммирования.
0 10000001011 1001000101000001110000101000111101011100001010001111
0 10000001011 1001000101000001110000101000111101011100001010010000
Разница видна в последних битах, и величина разницы составляет единицу последнего бита (с учётом величины числа, это 2-40).

При вычитании первого из второго как раз и получается 2-40, что составляет 0,0000000000009094947017729282379150390625.

Интересно посмотреть на процесс округления в данном случае. Истинная сумма A1 и A2 могла бы быть округлена как в большую, так и в меньшую сторону; но поскольку стандарт требует (по умолчанию) округление до ближайшего представимого значения, если таковое есть, то принимается во внимание разница между вычисленным значением и двумя представимыми значениями:


Меньшее представимое значение:
6420,10999999999967258190736174583435058593750
Разность:
0,00000000000045474735088646411895751953125
Истинный результат сложения:
6420,11000000000012732925824820995330810546875
Разность:
0,00000000000045474735088646411895751953125
Большее представимое значение:
6420,11000000000058207660913467407226562500000

Как видно, вычисленное значение оказалось точно между двумя представимыми значениями, и ни одно из них не ближе другого; тогда принимается во внимание следующее требование стандарта по умолчанию - округление до чётного ("банковское"). Как видно из двоичного представления представимых значений выше, мантисса большего из них оканчивается нулём - то есть является чётной. Поэтому оно и выбрано в качестве окончательного значения, возвращаемого сложением.
С уважением,
Михаил Каганский

sokol92

Михаил, спасибо за пример - будет, на что ссылаться.  :)

В дополнение можно рекомендовать эссе от авторов Python.
Владимир.

ost

#9
Содержательно. Благодарю за столь подробное пояснение и направление решения проблемы.
Завтра перекрою код текущей задачи. И не только текущей =)