Round (transact-sql)round (transact-sql)

3 Потеря точности при работе с вещественными числами

При работе с вещественными числами всегда нужно иметь в виду, что вещественные числа не точные. Всегда будут ошибки округления, ошибки преобразования из десятичной системы в двоичную и, наконец, самое частое – потеря точности при сложении/вычитании чисел слишком разных размерностей.

Последнее — самая неожиданная ситуация для новичков в программировании.

Если из числа вычесть , мы получим опять .

Вычитание чисел слишком разных размерностей Объяснение
Второе число слишком маленькое, и его значащая часть игнорируется (выделено серым). Оранжевым выделены 15 значащих цифр.

Что тут сказать, программирование — это не математика.

Округление в большую сторону при условии в Excel

Пример 2. Балл за предмет в учебном заведении рассчитывается как среднее арифметическое баллов за 7 контрольных работ. При этом в пользу ученика округление в большую сторону выполняется в том случае, если дробная часть полученного числа >= 0,8. Определить оценку для ученика (используется 10-бальная шкала оценки знаний).

Исходные данные:

Формула для расчета:

То есть, если дробная часть числа, которое является средним арифметическим значением баллов за 7 контрольных, меньше, чем 0,8, итоговый балл будет рассчитан как ближайшее меньшее целое среднего балла, иначе – как ближайшее большее (в пользу ученика).

Результат вычислений:

Ученик не получил «прибавку» к оценке, поскольку средний балл за контрольные составил 7,71 (0,71<0,8).

Встроенные функции

Для операции округления в Python есть встроенные функции – и

round

– округляет число (number) до ndigits знаков после запятой. Это стандартная функция, которая для выполнения не требует подключения модуля math.

По умолчанию операция проводится до нуля знаков – до ближайшего целого числа. Например:

Чтобы получить целый показатель, результат преобразовывают в .

Синтаксически функция вызывается двумя способами.

  1. – это округление числа до целого, которое расположено ближе всего. Если дробная часть равна 0,5, то округляют до ближайшего четного значения.
  2. – данные округляют до знаков после точки. Если округление проходит до сотых, то равен «2», если до тысячных – «3» и т.д.

int

– встроенная функция, не требующая подключения дополнительных модулей. Её функция – преобразование действительных значений к целому путем округления в сторону нуля. Например

Для положительных чисел функция аналогична функции , а для отрицательных – аналогично . Например:

Чтобы число по int преобразовать по математическим правилам, нужно выполнить следующие действия.

  1. Если число положительное, добавить к нему 0,5.
  2. Если число отрицательное, добавить -0,5.

Синтаксически преобразование оформляется так:

Применения

Округление используется для того, чтобы работать с числами в пределах того количества знаков, которое соответствует реальной точности параметров вычислений (если эти значения представляют собой измеренные тем или иным образом реальные величины), реально достижимой точности вычислений либо желаемой точности результата. В прошлом округление промежуточных значений и результата имело прикладное значение (так как при расчётах на бумаге или с помощью примитивных устройств типа абака учёт лишних десятичных знаков может серьёзно увеличить объём работы). Сейчас оно остаётся элементом научной и инженерной культуры. В бухгалтерских приложениях, кроме того, использование округлений, в том числе промежуточных, может требоваться для защиты от вычислительных ошибок, связанных с конечной разрядностью вычислительных устройств.

Более того, некоторые исследования используют округления возраста для измерения числовой грамотности. Это связано с фактом, что менее образованные люди склонны округлять свой возраст вместо того, что бы указывать точный. Например, в официальных записях населения с более низким уровнем человеческого капитала чаще встречается возраст 30, чем 31 или 29.

Примечания

  1. Кнут Д. Э. Искусство программирования. Том 1. Основные алгоритмы = The Art of Computer Programming. Volume 1. Fundamental Algorithms / под ред. С. Г. Тригуб (гл. 1), Ю. Г. Гордиенко (гл. 2) и И. В. Красикова (разд. 2.5 и 2.6). — 3. — Москва: Вильямс, 2002. — Т. 1. — 720 с. — ISBN 5-8459-0080-8.
  2. A’HEARN, B., J. BATEN AND D. CRAYEN (2009). “Quantifying Quantitative Literacy: Age Heaping and the History of Human Capital”, Journal of Economic History 69,783-808.
  3. В. М. Заварыкин, В. Г. Житомирский, М. П. Лапчик. Техника вычислений и алгоритмизация: Вводный курс: Учебное пособие для студентов педагогических институтов по физико-математическим специальностям. — М: Просвещение, 1987. 160 с.: ил.
  4. цит. по В. Гильде, З. Альтрихтер. «С микрокалькулятором в руках». Издание второе. Перевод с немецкого Ю. А. Данилова. М:Мир, 1987, стр. 64.

Округление при работе с числами ограниченной точности

Реальные физические величины всегда измеряются с некоторой конечной точностью, которая зависит от приборов и методов измерения и оценивается максимальным относительным или абсолютным отклонением неизвестного истинного значения от измеренного, что в десятичном представлении значения соответствует либо определённому числу значащих цифр, либо определённой позиции в записи числа, все цифры после (правее) которой являются незначащими (лежат в пределах погрешности измерения). Сами измеренные параметры записываются с таким числом знаков, чтобы все цифры были надёжными, возможно, последняя — сомнительной. Погрешность при математических операциях с числами ограниченной точности сохраняется и изменяется по известным математическим законам, поэтому когда в дальнейших вычислениях возникают промежуточные значения и результаты с больши́м числом цифр, из этих цифр только часть являются значимыми. Остальные цифры, присутствуя в значениях, фактически не отражают никакой физической реальности и лишь отнимают время на вычисления. Вследствие этого промежуточные значения и результаты при вычислениях с ограниченной точностью округляют до того количества знаков, которое отражает реальную точность полученных значений. На практике обычно рекомендуется при длинных «цепочных» ручных вычислениях сохранять в промежуточных значениях на одну цифру больше. При использовании компьютера промежуточные округления в научно-технических приложениях чаще всего теряют смысл, и округляется только результат.

Так, например, если задана сила 5815 гс с точностью до грамма силы и длина плеча 1,40 м с точностью до сантиметра, то момент силы в кгс по формуле M=(mg)⋅h{\displaystyle M=(mg)\cdot h}, в случае формального расчёта со всеми знаками, окажется равным: 5,815 кгс • 1,4 м = 8,141 кгс•м. Однако если учесть погрешность измерения, то мы получим, что предельная относительная погрешность первого значения составляет 1/5815 ≈ 1,7•10−4, второго — 1/140 ≈ 7,1•10−3, относительная погрешность результата по правилу погрешности операции умножения (при умножении приближённых величин относительные погрешности складываются) составит 7,3•10−3, что соответствует максимальной абсолютной погрешности результата ±0,059 кгс•м! То есть в реальности, с учётом погрешности, результат может составлять от 8,082 до 8,200 кгс•м, таким образом, в рассчитанном значении 8,141 кгс•м полностью надёжной является только первая цифра, даже вторая — уже сомнительна! Корректным будет округление результата вычислений до первой сомнительной цифры, то есть до десятых: 8,1 кгс•м, или, при необходимости более точного указания рамок погрешности, представить его в виде, округлённом до одного-двух знаков после запятой с указанием погрешности: 8,14 ± 0,06 кгс•м.

Округление рассчитанного значения погрешности

Обычно в окончательном значении рассчитанной погрешности оставляют только первые одну-две значащие цифры. По одному из применяемых правил, если значение погрешности начинается с цифр 1 или 2(по другому правилу — 1, 2 или 3), то в нём сохраняют две значащих цифры, в остальных случаях — одну, например: 0,13; 0,26; 0,3; 0,8. То есть каждая декада возможных значений округляемой погрешности разделена на две части. Недостаток этого правила состоит в том, что относительная погрешность округления изменяется значительным скачком при переходе от числа 0,29 к числу 0,3. Для устранения этого предлагается каждую декаду возможных значений погрешности делить на три части с менее резким изменением шага округления. Тогда ряд разрешённых к употреблению округлённых значений погрешности получает вид:

  • 0,10; 0,12; 0,14; 0,16; 0,18;
  • 0,20; 0,25; 0,30; 0,35; 0,40; 0,45;
  • 0,5; 0,6; 0,7; 0,8; 0,9; 1,0.

Однако при использовании такого правила последние цифры самого результата, оставляемые после округления, также должны соответствовать приведённому ряду.

Пересчёт значений физических величин

Пересчёт значения физической величины из одной системы единиц в другую должен производиться с сохранением точности исходного значения. Для этого исходное значение в одних единицах следует умножить (разделить) на переводной коэффициент, часто содержащий большое количество значащих цифр, и округлить полученный результат до количества значащих цифр, обеспечивающего точность исходного значения. Например, при пересчёте значения силы 96,3 тс в значение, выраженное в килоньютонах (кН), следует умножить исходное значение на переводной коэффициент 9,80665 (1 тс = 9,80665 кН). В результате получается значение 944,380395 кН, которое необходимо округлить до трёх значащих цифр. Вместо 96,3 тс получаем 944 кН.

Варианты округления 0,5 к ближайшему целому

Отдельного описания требуют правила округления для специального случая, когда (N+1)-й знак = 5, а последующие знаки равны нулю. Если во всех остальных случаях округление до ближайшего целого обеспечивает меньшую погрешность округления, то данный частный случай характерен тем, что для однократного округления формально безразлично, производить его «вверх» или «вниз» — в обоих случаях вносится погрешность ровно в 1/2 младшего разряда. Существуют следующие варианты правила округления до ближайшего целого для данного случая:

  • Математическое округление — округление всегда в бо́льшую по модулю сторону (предыдущий разряд всегда увеличивается на единицу).
  • Банковское округление (англ. banker’s rounding) — округление для этого случая происходит к ближайшему чётному, то есть 2,5 → 2; 3,5 → 4.
  • Случайное округление — округление происходит в меньшую или большую сторону в случайном порядке, но с равной вероятностью (может использоваться в статистике). Также часто используется округление с неравными вероятностями (вероятность округления вверх равна дробной части), этот способ делает накопление ошибок случайной величиной с нулевым математическим ожиданием.
  • Чередующееся округление — округление происходит в меньшую или большую сторону поочерёдно.

Во всех вариантах в случае, когда (N+1)-й знак не равен 5 или последующие знаки не равны нулю, округление происходит по обычным правилам: 2,49 → 2; 2,51 → 3.

Математическое округление просто формально соответствует общему правилу округления (см. выше). Его недостатком является то, что при округлении большого числа значений, которые далее будут обрабатываться совместно, может происходить накопление ошибки округления. Типичный пример: округление до целых рублей денежных сумм, выражаемых в рублях и копейках. В реестре из 10 000 строк (если считать копеечную часть каждой суммы случайным числом с равномерным распределением, что обычно вполне допустимо) окажется в среднем около 100 строк с суммами, содержащими в части копеек значение 50. При округлении всех таких строк по правилам математического округления «вверх» сумма «итого» по округлённому реестру окажется на 50 рублей больше точной.

Три остальных варианта как раз и придуманы для того, чтобы уменьшить общую погрешность суммы при округлении большого количества значений. Округление «до ближайшего чётного» исходит из предположения, что при большом числе округляемых значений, имеющих 0,5 в округляемом остатке, в среднем половина из них окажется слева, а половина — справа от ближайшего чётного, таким образом, ошибки округления взаимно погасятся. Строго говоря, предположение это верно лишь тогда, когда набор округляемых чисел обладает свойствами случайного ряда, что обычно верно в бухгалтерских приложениях, где речь идёт о ценах, суммах на счетах и так далее. Если же предположение будет нарушено, то и округление «до чётного» может приводить к систематическим ошибкам. Для таких случаев лучше работают два следующих метода.

Два последних варианта округления гарантируют, что примерно половина специальных значений будет округлена в одну сторону, половина — в другую. Но реализация таких методов на практике требует дополнительных усилий по организации вычислительного процесса.

  • Округление в случайную сторону требует для каждой округляемой строки генерировать случайное число. При использовании псевдослучайных чисел, создаваемых линейным реккурентным методом, для генерации каждого числа требуется операция умножения, сложения и деления по модулю, что для больших объёмов данных может существенно замедлить расчёты.
  • Чередующееся округление требует хранить флаг, показывающий, в какую сторону последний раз округлялось специальное значение, и при каждой операции переключать значение этого флага.

АргументыArguments

numeric_expressionnumeric_expressionВыражение категории точного числового или приблизительного числового типа данных, за исключением типа данных bit.Is an expression of the exact numeric or approximate numeric data type category, except for the bit data type.

lengthlengthТочность, с которой должно быть округлено значение numeric_expression.Is the precision to which numeric_expression is to be rounded. Аргумент length должен быть выражением типа tinyint, smallint или int. Если аргумент length является положительным числом, значение numeric_expression округляется до числа десятичных разрядов, указанных в аргументе length.length must be an expression of type tinyint, smallint, or int. When length is a positive number, numeric_expression is rounded to the number of decimal positions specified by length. Если аргумент length является отрицательным числом, значение numeric_expression округляется слева от десятичной запятой, как указано в аргументе length.When length is a negative number, numeric_expression is rounded on the left side of the decimal point, as specified by length.

functionfunctionТип выполняемой операции.Is the type of operation to perform. Аргумент function должен иметь тип tinyint, smallint или int. Если аргумент function не указан или имеет значение 0 (по умолчанию), значение numeric_expression округляется.function must be tinyint, smallint, or int. When function is omitted or has a value of 0 (default), numeric_expression is rounded. Когда указывается значение, не равное 0, значение numeric_expression усекается.When a value other than 0 is specified, numeric_expression is truncated.

ПримерыExamples

A.A. Использование функции ROUND и приближенийUsing ROUND and estimates

Следующий пример показывает два выражения, которые демонстрируют, используя , что последний знак всегда является приближением.The following example shows two expressions that demonstrate by using the last digit is always an estimate.

Результирующий набор:Here is the result set.

В следующем примере показаны округление и аппроксимация.The following example shows rounding and approximations.

Результирующий набор:Here is the result set.

В.C. Использование функции ROUND для усеченияUsing ROUND to truncate

В следующем примере используются две инструкции для демонстрации различия между округлением и усечением.The following example uses two statements to demonstrate the difference between rounding and truncation. Первая инструкция округляет результат.The first statement rounds the result. Вторая инструкция усекает результат.The second statement truncates the result.

Результирующий набор:Here is the result set.

RemarksRemarks

Функция ROUND всегда возвращает значение.ROUND always returns a value. Если аргумент length имеет отрицательное значение и больше числа знаков перед десятичной запятой, ROUND возвращает 0.If length is negative and larger than the number of digits before the decimal point, ROUND returns 0.

ПримерExample РезультатResult
ROUND(748,58, -4)ROUND(748.58, -4)

Функция ROUND возвращает округленное значение выражения numeric_expression независимо от типа данных, когда length является отрицательным числом.ROUND returns a rounded numeric_expression, regardless of data type, when length is a negative number.

ПримерыExamples РезультатResult
ROUND(748,58, -1)ROUND(748.58, -1) 750,00750.00
ROUND(748,58, -2)ROUND(748.58, -2) 700,00700.00
ROUND(748.58, -3)ROUND(748.58, -3) В результате возникает арифметическое переполнение, так как для значения 748,58 по умолчанию используется тип decimal (5,2), который не позволяет вернуть значение 1000.Results in an arithmetic overflow, because 748.58 defaults to decimal(5,2), which cannot return 1000.00.
Чтобы округлить результат до четырех цифр, измените тип данных на входе.To round up to 4 digits, change the data type of the input. Пример:For example: 1000.001000.00

Округление дат и времени

Обратите внимание, что тип Дата/Время в Аксессе является особым видом типа с плавающей запятой, в котором дробная часть обозначает время дня. Следовательно, поля типа Дата/Время с компонентой времени также подвержены ошибкам округления

Функция ниже округляет дату/время до указанного количества секунд. Например, чтобы округлить до ближайшего получаса (30 * 60 seconds), используйте:     =RoundTime(, 1800)

Public Function RoundTime(varTime As Variant, Optional ByVal lngSeconds As Long = 900&) As Variant    'Цель:   Округлить величину дата/время до ближайшего количества указанных секунд      'Аргументы: varTime = величина дата/время      '           lngSeconds = количество секунд, до которых требуется округлить.      '               напр.  60 для ближайшей минуты,      '                    600 для ближайших 10 минут,      '                   3600 для ближайшего часа,      '                  86400 до следующего дня.      'Возвращает:    Округленное значение дата/время либо Null, если не было входного аргумента дата/время.      'Примечание:      lngSeconds должно быть между 1 и 86400.      '           Округляет по умолчанию до ближайших 15 minutes.      Dim lngSecondsOffset As Long            RoundTime = Null        'Инициализируем возвращаемый Null.      If Not IsError(varTime) Then          If IsDate(varTime) Then              If (lngSeconds < 1&) Or (lngSeconds > 86400) Then                  lngSeconds = 1&              End If              lngSecondsOffset = lngSeconds * CLng(DateDiff("s", #12:00:00 AM#, TimeValue(varTime)) / lngSeconds)              RoundTime = DateAdd("s", lngSecondsOffset, DateValue(varTime))          End If      End If  End Function

Неточные вычисления

Внутри JavaScript число представлено в виде 64-битного формата IEEE-754. Для хранения числа используется 64 бита: 52 из них используется для хранения цифр, 11 из них для хранения положения десятичной точки (если число целое, то хранится 0), и один бит отведён на хранение знака.

Если число слишком большое, оно переполнит 64-битное хранилище, JavaScript вернёт бесконечность:

Наиболее часто встречающаяся ошибка при работе с числами в JavaScript – это потеря точности.

Посмотрите на это (неверное!) сравнение:

Да-да, сумма и не равна .

Странно! Что тогда, если не ?

Но почему это происходит?

Число хранится в памяти в бинарной форме, как последовательность бит – единиц и нулей. Но дроби, такие как , , которые выглядят довольно просто в десятичной системе счисления, на самом деле являются бесконечной дробью в двоичной форме.

Другими словами, что такое ? Это единица делённая на десять — , одна десятая. В десятичной системе счисления такие числа легко представимы, по сравнению с одной третьей: , которая становится бесконечной дробью .

Деление на гарантированно хорошо работает в десятичной системе, но деление на – нет. По той же причине и в двоичной системе счисления, деление на обязательно сработает, а становится бесконечной дробью.

В JavaScript нет возможности для хранения точных значений 0.1 или 0.2, используя двоичную систему, точно также, как нет возможности хранить одну третью в десятичной системе счисления.

Числовой формат IEEE-754 решает эту проблему путём округления до ближайшего возможного числа. Правила округления обычно не позволяют нам увидеть эту «крошечную потерю точности», но она существует.

Пример:

И когда мы суммируем 2 числа, их «неточности» тоже суммируются.

Вот почему – это не совсем .

Не только в JavaScript

Справедливости ради заметим, что ошибка в точности вычислений для чисел с плавающей точкой сохраняется в любом другом языке, где используется формат IEEE 754, включая PHP, Java, C, Perl, Ruby.

Можно ли обойти проблему? Конечно, наиболее надёжный способ — это округлить результат используя метод toFixed(n):

Также можно временно умножить число на 100 (или на большее), чтобы привести его к целому, выполнить математические действия, а после разделить обратно. Суммируя целые числа, мы уменьшаем погрешность, но она все равно появляется при финальном делении:

Таким образом, метод умножения/деления уменьшает погрешность, но полностью её не решает.

Забавный пример

Попробуйте выполнить его:

Причина та же – потеря точности. Из 64 бит, отведённых на число, сами цифры числа занимают до 52 бит, остальные 11 бит хранят позицию десятичной точки и один бит – знак. Так что если 52 бит не хватает на цифры, то при записи пропадут младшие разряды.

Интерпретатор не выдаст ошибку, но в результате получится «не совсем то число», что мы и видим в примере выше. Как говорится: «как смог, так записал».

Два нуля

Другим забавным следствием внутреннего представления чисел является наличие двух нулей: и .

Все потому, что знак представлен отдельным битом, так что, любое число может быть положительным и отрицательным, включая нуль.

В большинстве случаев это поведение незаметно, так как операторы в JavaScript воспринимают их одинаковыми.

ОКРУГЛЕНИЕ К БЛИЖАЙШЕМУ ЦЕЛОМУ

Округление к ближайшему целому до N-го знака осуществляется по следующему правилу:

  • если N+1 знак < 5, то N-ый знак остается без изменений, а все знаки после N-го отбрасываются (обнуляются);
  • если N+1 знак > 5, то N-ый знак увеличивают на единицу, а все знаки после N-го отбрасываются (обнуляются).

Примеры округления до 2 знаков после запятой:

2.4545 → 2.452.4564 → 2.46

По способам округления числа в случае когда N+1 знак равен 5, выделяются следующие виды округления к ближайшему целому:

  • Математическое округление;
  • Банковское округление;
  • Случайное округление;
  • Чередующееся округление.

Математическое округление в случае если N+1 знак = 5 увеличивает N-й знак на единицу,  а все знаки после N-го отбрасываются (обнуляются). 

Пример математического округления до 2-х знаков после запятой:

2.4554 → 2.46

Данное округление в ABL реализовано в функции ROUND.

ROUND(iRnd, n)

  • iRnd — округляемое значение;
  • n — знак до которого осуществляется округление.

Банковское округление отличается от математического тем, что предполагает округление в таком случае к ближайшему четному числу. Т.е. результатом округления числа 2.5 при математическом округлении будет 3, а при банковском 2. 

FUNCTION BankRound RETURNS DECIMAL (INPUT iRnd AS DEC, INPUT n AS INT).    DEF VAR ChkFor5 AS INTEGER NO-UNDO.   DEF VAR B_Round AS DECIMAL NO-UNDO.   B_Round = ROUND(iRnd,n).   ChkFor5 = ROUND(((TRUNCATE(iRnd, n + 1) -                      TRUNCATE(iRnd,n)) * EXP(10, n + 1)),0).   IF ChkFor5 = 5 THEN    DO:      IF ((TRUNCATE(iRnd,n) * EXP(10,n)) MOD 2) = 0 THEN          B_Round = TRUNCATE(i,n).   END.   RETURN B_Round.END FUNCTION.

Случайное округление осуществляет равновероятное округление числа 5 как в меньшую (N-ый знак остается без изменений) так и в большую (N-ый знак увеличивают на единицу) стороны. Например, в момент округления значения можно генерировать случайное целое число в пределах . Если полученное число равно нулю, то округление осуществляется в меньшую сторону, если единице, то в большую.

FUNCTION RandomRound RETURNS DECIMAL (INPUT iRnd AS DEC, INPUT n AS INT).   DEF VAR vResult AS DECIMAL NO-UNDO.   DEF VAR ChkFor5 AS INTEGER NO-UNDO.   DEF VAR vRandom  AS DECIMAL NO-UNDO.   vRandom = RANDOM(0,1).   vResult = TRUNCATE(iRnd,n).   ChkFor5 = ROUND(((TRUNCATE(iRnd, n + 1) -                      TRUNCATE(iRnd,n)) * EXP(10, n + 1)),0).   IF ChkFor5 = 5 THEN vResult = vResult + vRandom * EXP(10, - n).    RETURN vResult.END FUNCTION. 

Чередующееся округление осуществляет округление числа 5 поочередно то в меньшую, то в большую стороны. Данное округление очевидно применимо при необходимости округления массива чисел, а не единичного числа.

Округлить до 1000 руб.

Функция Round() в Excel принимает отрицательные числа в качестве количества мест от запятой, напр. Round(123456, -3) округляет до 1000. К сожалению, аксессовская функция этого не поддерживает.

Чтобы округлить до ближайших 1000 руб., разделите на 1000, округлите, и умножьте на 1000. Пример:    1000 * Round( / 1000, 0)

Чтобы округлить до 1000 руб. вниз, разделите на 1000, получите целое число и умножьте на 1000. Пример:    1000 * Int( / 1000)

Чтобы округлить до верхней 1000 руб., разделите на 1000 и умножьте на -1 перед получением целой величины. Пример:    -1000 * Int( / -1000)

Чтобы округлить в сторону нуля, используйте Fix() вместо Int().

Альтернативно, пользовательская функция Кена Гетца ведет себя в точности как упомнутая экселевская функция.

Ошибки плавающей запятой

Дробные величины компьютер обычно трактует как числа с плавающей точкой. Аксессовские поля типов Двойной точности (Double) или Одинарной точности (Single) относятся к такому типу. Тип «Двойной точности» дает около 15 знаков точности, сингл — 8 знаков (подобно ручному калькулятору).

Но эти числа являются приблизительными. Точно так же, как 1/3 требует бесконечного количества знаков в десятичной системе, большинство чисел с плавающей запятой не могут быть представлены точно в двоичной системе. Википедия объясняет , с которой вы сталкиваетесь, оперируя числами с плавающей запятой.

Резюме заключается в том, что крайние цифры могут не округлиться ожидаемым вами образом,благодаря тому факту, что действительные значения и отображаемые не совпадают. Это становится особенно заметно при проверке банковского округления.

Один из способов избежать подобных проблем — использовать числа с фиксированной запятой или мастшабированные числа.Тип данных «Денежный» в Аксессе является типом с фиксированной запятой: он всегда хранит 4 десятичных знака.

Например, откройте окно Immediate Window (Ctrl+G) и введите:    ? Round(CCur(.545),2), Round(CDbl(.545),2) Денежный тип (первый) возвращает 0,54, тогда как Двойной точности — 0,55. Денежный округляет корректно (к четной цифре 4); тип с плавающей запятой (Двойной точности) некорректно. Подобным образом, если вы попробуете 8,995, Денежный корректно округлит вверх (к четной цифре 0), в то время как тип Двойной точности округлит вниз (неверно.)

Денежный тип справляется только с 4 десятичными знаками. Используйте масштабируемый тип Действительный (Decimal), если вам нужно больше знаков после запятой.

Заключение

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

Думаю, команда Go приняла правильное решение, добавив функцию Round() в стандартную библиотеку. Без этого мы бы продолжали пользоваться различными некорректными реализациями.

Надеюсь, теперь вам стало ясно, что при работе с float есть много подводных камней, про которые порой забывают даже эксперты. Легко придумать или скопировать откуда-то однострочную реализацию, но сложно написать действительно корректную. Неудивительно, что корректно работающее округление появилось лишь в шестой мажорной версии Java (через 15 лет, прошедших с релиза Java 1.0 до выхода Java 7), и я рад, что Go прошёл этот путь быстрее.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector