Всем доброго дня! Столкнулся с небольшой проблемой, непонятной для меня. Подскажите. пожалуйста, почему, когда я задаю конечное дробное число, возникают погрешности в приведённом ниже примере? Какие есть способы борьбы с этим (помимо Round())? [vba]
Код
Sub test1() Dim a As Double Dim b As Single a = 1234.01 b = 1234.01 MsgBox (a - Fix(a)) MsgBox (b - Fix(b)) End Sub
[/vba]
Всем доброго дня! Столкнулся с небольшой проблемой, непонятной для меня. Подскажите. пожалуйста, почему, когда я задаю конечное дробное число, возникают погрешности в приведённом ниже примере? Какие есть способы борьбы с этим (помимо Round())? [vba]
Код
Sub test1() Dim a As Double Dim b As Single a = 1234.01 b = 1234.01 MsgBox (a - Fix(a)) MsgBox (b - Fix(b)) End Sub
Эта проблема встречается в разных языках программирования. Я видел на примере семейства Java. Проблема связана с тем, что сложно закодировать дробное число с плавающей запятой с помощью бинарного кода на машинном уровне.
Предлагаю такой подход, похоже, работает для типа Double:
[vba]
Код
Sub Test() Dim a As Double Dim b As Single Const xCheck& = 1000000 a = 1234.01 b = 1234.01 a = a * xCheck b = b * xCheck Debug.Print (a - (a \ xCheck) * xCheck) / xCheck ' Вернёт 0.01 Debug.Print (b - (b \ xCheck) * xCheck) / xCheck ' Вернёт 0.009984 End Sub
[/vba]
Roman777, привет!
Эта проблема встречается в разных языках программирования. Я видел на примере семейства Java. Проблема связана с тем, что сложно закодировать дробное число с плавающей запятой с помощью бинарного кода на машинном уровне.
Предлагаю такой подход, похоже, работает для типа Double:
[vba]
Код
Sub Test() Dim a As Double Dim b As Single Const xCheck& = 1000000 a = 1234.01 b = 1234.01 a = a * xCheck b = b * xCheck Debug.Print (a - (a \ xCheck) * xCheck) / xCheck ' Вернёт 0.01 Debug.Print (b - (b \ xCheck) * xCheck) / xCheck ' Вернёт 0.009984 End Sub
Roman777, Excel тоже считает как тип Double, поэтому можно наверное не переживать, если макрос неправильно подсчитает, т.к. Excel тоже неправильно бы подсчитал.
Roman777, Excel тоже считает как тип Double, поэтому можно наверное не переживать, если макрос неправильно подсчитает, т.к. Excel тоже неправильно бы подсчитал.Karataev
Саня, ощутимее, всё же мне стало вот на таком примере (ну это в сравнении с Вашим первоначальным): [vba]
Код
Sub test1() Dim a As Double Dim b As Single a = 1234.04687 ' 1234.01 b = 1234.04687 ' 1234.01 MsgBox (a - Fix(a)) 'вернет 4,68699999998989E-02 MsgBox (b - Fix(b)) 'вернет 0,046875 End Sub
[/vba]
Саня, ощутимее, всё же мне стало вот на таком примере (ну это в сравнении с Вашим первоначальным): [vba]
Код
Sub test1() Dim a As Double Dim b As Single a = 1234.04687 ' 1234.01 b = 1234.04687 ' 1234.01 MsgBox (a - Fix(a)) 'вернет 4,68699999998989E-02 MsgBox (b - Fix(b)) 'вернет 0,046875 End Sub
Вы не указали сущность, для которой производятся вычисления. Если это деньги (цена, сумма), то лучше использовать денежный формат. Если это например нормочасы или нормы расхода, расценки и т.д, то нужно перед арифметическим действием умножать оба числа на 10 в степени дробной точности с округлением до целого каждого из "слагаемых", а потом результат делить на эту же спепень с округлением до нужной степени точности. При умножении делитель должен быть как произведение точностей обоих слагаемых.
Если у Вас делаюся агрегатные вычисления (например, сумма по счету-фактуре, сметная стоимость, товарные отчеты и т.д.), то перечисленные действия необходимо выполнять как для каждой строки, так и при каждом построчном сложении. Такая технология даст практически 100% гарантию точного совпадения подсчитанных результатов с теми, которые условный бухгалтер или сметчик получает "на калькуляторе".
Roman777,
Вы не указали сущность, для которой производятся вычисления. Если это деньги (цена, сумма), то лучше использовать денежный формат. Если это например нормочасы или нормы расхода, расценки и т.д, то нужно перед арифметическим действием умножать оба числа на 10 в степени дробной точности с округлением до целого каждого из "слагаемых", а потом результат делить на эту же спепень с округлением до нужной степени точности. При умножении делитель должен быть как произведение точностей обоих слагаемых.
Если у Вас делаюся агрегатные вычисления (например, сумма по счету-фактуре, сметная стоимость, товарные отчеты и т.д.), то перечисленные действия необходимо выполнять как для каждой строки, так и при каждом построчном сложении. Такая технология даст практически 100% гарантию точного совпадения подсчитанных результатов с теми, которые условный бухгалтер или сметчик получает "на калькуляторе".SGerman
Мудрость приходит со старостью. Но иногда старость приходит одна :)
Когда-то это была проблема - при подсчете "Итого" в счетах-фактурах или накладных на отпуск итоговая сумма не сходилась с той, которую получал бухгалтер на калькуляторе (были и такие бабушки, которые лихо щелкали на счетах) - приходилось вышеописанную методу вставлять в программы, даже написал для этого небольшую библиотеку (база была на Paradox, позже Delphi+Access) При переходе на SQL-сервер проблема исчезла, т.к. там умные дяденьки предусмотрели нужные форматы данных
В дополнение:
Когда-то это была проблема - при подсчете "Итого" в счетах-фактурах или накладных на отпуск итоговая сумма не сходилась с той, которую получал бухгалтер на калькуляторе (были и такие бабушки, которые лихо щелкали на счетах) - приходилось вышеописанную методу вставлять в программы, даже написал для этого небольшую библиотеку (база была на Paradox, позже Delphi+Access) При переходе на SQL-сервер проблема исчезла, т.к. там умные дяденьки предусмотрели нужные форматы данныхSGerman
Мудрость приходит со старостью. Но иногда старость приходит одна :)
SGerman, Принципиально не важно было для какого-то случая. Тут работа была с цифрами не имеющими физических (или денежных) интерпретаций. Сам тоже думал о втором варианте, предложенном Вами, но мне показалось это извращением, поэтому спросил...) Спасибо за предложения.
SGerman, Принципиально не важно было для какого-то случая. Тут работа была с цифрами не имеющими физических (или денежных) интерпретаций. Сам тоже думал о втором варианте, предложенном Вами, но мне показалось это извращением, поэтому спросил...) Спасибо за предложения.Roman777
Roman777, Переход на SQL-сервер решает эту проблему полностью. Excel, на мой непросвященный взгляд, должен служить лишь "тонким" клиентом для реализации интерфейса с БД. Все вычисления, проверки и т.д. должны выполняться именно на серверу, для этого там есть бизнес-логика и куча нештяков. Тем более, что сейчас нет никаких проблем с выбором абсолютно бесплатных серверов.
Имея немалый опыт разработки различных СУБД-проектов, давно пришел к убеждению, что для формирования всевозможных отчетов (в т.ч. и не очень-то табличных) ничего лучше Excel еще не придумано.
Roman777, Переход на SQL-сервер решает эту проблему полностью. Excel, на мой непросвященный взгляд, должен служить лишь "тонким" клиентом для реализации интерфейса с БД. Все вычисления, проверки и т.д. должны выполняться именно на серверу, для этого там есть бизнес-логика и куча нештяков. Тем более, что сейчас нет никаких проблем с выбором абсолютно бесплатных серверов.
Имея немалый опыт разработки различных СУБД-проектов, давно пришел к убеждению, что для формирования всевозможных отчетов (в т.ч. и не очень-то табличных) ничего лучше Excel еще не придумано.SGerman
Мудрость приходит со старостью. Но иногда старость приходит одна :)