round , roundf , roundl
Округляет значение с плавающей запятой до ближайшего целочисленного значения.
Синтаксис
double round( double x ); float round( float x ); // C++ only long double round( long double x ); // C++ only float roundf( float x ); long double roundl( long double x ); #define round(X) // Requires C11 or higher
Параметры
x
Округляемое значение с плавающей запятой.
Возвращаемое значение
Функции round возвращают значение с плавающей запятой, которое представляет целое число, ближайшее к x . Промежуточные значения округляются в сторону от нуля, независимо от настройки режима округления чисел с плавающей запятой. Не возвращается ошибка.
| Входные данные | Исключение SEH | Исключение _matherr |
|---|---|---|
| ± QNaN, IND | нет | _DOMAIN |
Замечания
Поскольку C++ допускает перегрузку, можно вызывать перегрузки round , которые принимают и возвращают значения float и long double . В программе C, если вы не используете макрос для вызова этой функции, round всегда принимает и возвращает значение double .
Если вы используете round макрос из , тип аргумента определяет, какая версия функции выбрана. Дополнительные сведения см . в разделе «Математика с универсальным типом».
По умолчанию глобальное состояние этой функции ограничивается приложением. Чтобы изменить это поведение, см . статью «Глобальное состояние» в CRT.
Требования
| Маршрут | Обязательный заголовок |
|---|---|
| round , roundf , roundl | |
| round Макрос |
Дополнительные сведения о совместимости см. в разделе Совместимость.
Пример
// Build with: cl /W3 /Tc // This example displays the rounded // results of floating-point values #include #include int main() < printf("===== Round a float\n\n"); float floatValue = 2.4999999f; // float stores a value close to, but not exactly equal to, the initializer below. floatValue will contain 2.5 because it is the closest single precision value printf("roundf(%.1000g) is %.1000g\n", floatValue, roundf(floatValue)); printf("roundf(%.1000g) is %.1000g\n", -floatValue, roundf(-floatValue)); // double stores a value close to, but not exactly equal to, the initializer below. The closest double value is just slightly larger. double doubleValue = 2.4999999; printf("\n===== Round a double\n\n"); printf("round(%.1000g) is %.1000g\n", doubleValue, round(doubleValue)); printf("round(%.1000g) is %.1000g\n", -doubleValue, round(-doubleValue)); // long double stores a value close to, but not exactly equal to, the initializer below. The closest long double value is just slightly larger. long double longDoubleValue = 2.4999999L; printf("\n===== Round a long double\n\n"); printf("roundl(%.1000g) is %.1000g\n", longDoubleValue, roundl(longDoubleValue)); printf("roundl(%.1000g) is %.1000g\n", -longDoubleValue, roundl(-longDoubleValue)); return 0; >
===== Round a float roundf(2.5) is 3 roundf(-2.5) is -3 ===== Round a double round(2.499999900000000163657887242152355611324310302734375) is 2 round(-2.499999900000000163657887242152355611324310302734375) is -2 ===== Round a long double roundl(2.499999900000000163657887242152355611324310302734375) is 2 roundl(-2.499999900000000163657887242152355611324310302734375) is -2
Math.Round: округлить число до 2 знаков
И хотим округлить его до двух знаков так, как нас этому учили в школе, справа налево. В результате должны получить 14.79:
Console.WriteLine(testDecimal); Console.WriteLine(Math.Round(testDecimal, 2, MidpointRounding.AwayFromZero)) //14,78;
Но реальность отказывается совпадать со школьными знаниями, и начинаем ее под эти школьные знания подгонять:
Console.WriteLine(Math.Round(testDecimal, 3, MidpointRounding.AwayFromZero)) //14,785; Console.WriteLine(Math.Round(Math.Round(testDecimal, 3, MidpointRounding.AwayFromZero), 2, MidpointRounding.AwayFromZero)) //14,79;
Учимся округлять в C#
А знаете ли вы, что Math.Round(1.5) == Math.Round(2.5) == 2 ? Можете ли сходу сказать, сколько будет -7%3 и 7%-3 ? Помните ли, чем отличаются Math.Round , Math.Floor , Math.Ceiling , Math.Truncate ? А как происходит округление при использовании string.Format ? Давайте немного погрузимся в мир округлений и разберёмся с нюансами, которые не для всех могут быть очевидными.
Math.Round
Math.Round — это метод округления к ближайшему числу или к ближайшему числу с заданным количеством знаков после запятой. Работает с типами decimal и double , в параметрах можно встретить три вида параметров:
- value : округляемое число
- digits : количество знаков в дробной части, которые нужно оставить
- mode : параметр, который определяет в какую сторону округлять число, которое находится ровно посередине между двумя вариантами
Параметр mode используется, когда округляемое значение находится ровно посередине между двумя вариантами. Принимает значение из следующего перечисления:
Обратите внимание, что по умолчанию mode == MidpointRounding.ToEven , поэтому Math.Round(1.5) == Math.Round(2.5) == 2 .
Math.Floor, Math.Ceiling, Math.Truncate
Сводная таблица
Сориентироваться в методах округления может помочь следующая табличка:
Округление проводится в соответствии со стандартом IEEE Standard 754, section 4.
Целочисленное деление и взятие по модулю
В C# есть два замечательных оператора над целыми числами: / для целочисленного деления (MSDN) и % для взятия остатка от деления (MSDN). Деление производится по следующим правилам:
- При целочисленном делении результат всегда округляется по направлению к нулю.
- При взятии остатка от деления должно выполняться следующее правило: x % y = x – (x / y) * y
Также можно пользоваться шпаргалкой:
string.Format
При форматировании чисел в виде строки можно пользоваться функцией string.Format (см. Standard Numeric Format Strings, Custom Numeric Format Strings). Например, для вывода числа с двумя знаками после десятичной точки можно воспользоваться string.Format(«», value) или string.Format(«», value) . Округление происходит по принципу AwayFromZero . Проиллюстрируем правила округления очередной табличкой:
", value) |
Задачи
На приведённую тему есть две задачки в ProblemBook.NET: Rounding1, Rounding2.
- GitHub
- RSS
Math. Round Метод
Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
Округляет значение до ближайшего целого или указанного количества десятичных знаков.
Перегрузки
Округляет значение двойной точности с плавающей запятой до указанного числа дробных цифр, используя указанное соглашение о округлении.
Округляет десятичное значение до указанного числа дробных цифр, используя указанное соглашение о округлении.
Округляет значение двойной точности с плавающей запятой до целого числа, используя указанное соглашение о округлении.
Округляет значение с плавающей запятой двойной точности до указанного числа знаков после запятой; значения посередине округляются до ближайшего четного числа.
Округляет десятичное значение до указанного числа знаков после запятой; значения посередине округляются до ближайшего четного числа.
Округляет значение с плавающей запятой двойной точности до ближайшего целого значения; значения посередине округляются до ближайшего четного числа.
Округляет десятичное значение до ближайшего целого значения; значения посередине округляются до ближайшего четного числа.
Округляет десятичное значение на целое число, используя указанное соглашение округления.
Примеры
В дополнение к примерам в разделе Примечания в этой статье содержатся примеры, иллюстрирующие следующие перегрузки Math.Round метода:
Комментарии
Какой метод вызывается?
Для выбора подходящего метода округления можно использовать следующую таблицу. Помимо Math.Round методов, он также включает Math.Ceiling и Math.Floor.
| Кому | Вызов |
|---|---|
| Округление числа до целого числа с помощью соглашения округления до ближайшего. | Round(Decimal) -или- Round(Double) |
| Округление числа до целого числа с помощью указанного соглашения об округлении. | Round(Decimal, MidpointRounding) -или- Round(Double, MidpointRounding) |
| Округление числа до указанного числа дробных цифр с помощью округления до ближайшего соглашения. | Round(Decimal, Int32) -или- Round(Double, Int32) |
| Округление числа до указанного числа дробных цифр с помощью указанного соглашения об округлении. | Round(Decimal, Int32, MidpointRounding) -или- Round(Double, Int32, MidpointRounding) |
| Single Округление значения до указанного числа дробных цифр с помощью заданного соглашения об округлении и минимизации потери точности. | Преобразуйте в SingleDecimal и вызовите Round(Decimal, Int32, MidpointRounding). |
| Округление числа до указанного числа дробных цифр при минимизации проблем с точностью округления значений середины. | Вызовите метод округления, реализующий сравнение «больше или приблизительно равно». См. статью Округление и точность. |
| Округление дробного значения до целого числа, которое больше дробного значения. Например, округление 3.1 до 4. | Ceiling |
| Округление дробного значения до целого числа, которое меньше дробного значения. Например, округление 3,9 до 3. | Floor |
Средние значения и соглашения о округлениях
Округление включает преобразование числового значения с заданной точностью в значение с меньшей точностью. Например, можно использовать Round(Double) метод для округления значения от 3,4 до 3,0, а Round(Double, Int32) метод — для округления значения от 3,579 до 3,58.
В средней точке значение после наименее значимой цифры в результате находится на полпути между двумя числами. Например, 3,47500 — это среднее значение, если оно должно быть округлено до двух десятичных знаков, а 7,500 — это среднее значение, если оно должно быть округлено до целого числа. В таких случаях, если используется стратегия округления до ближайшего, ближайшее значение невозможно определить без соглашения об округлении.
Метод Round поддерживает два соглашения округления для обработки значений середины:
- Округление от нуля Средние значения округляются до следующего числа от нуля. Например, 3,75 округляется до 3,8, 3,85 округляется до 3,9, -3,75 округляется до -3,8, а -3,85 округляется до -3,9. Эта форма округления представлена элементом MidpointRounding.AwayFromZero перечисления.
- Округление до ближайшего четного или округление банкира Средние значения округляются до ближайшего четного числа. Например, значения 3,75 и 3,85 округлялись до 3,8, а значения -3,75 и -3,85 округлялись до -3,8. Эта форма округления представлена элементом MidpointRounding.ToEven перечисления.
В .NET Core 3.0 и более поздних версиях с помощью перечисления MidpointRounding доступны три дополнительные стратегии округления. Эти стратегии используются во всех случаях, а не только для средних значений, как MidpointRounding.ToEven и MidpointRounding.AwayFromZero есть.
Округление от нуля является наиболее широко известной формой округления, в то время как округление до ближайшего четного является стандартом в финансовых и статистических операциях. Он соответствует стандарту IEEE 754, раздел 4. При использовании в нескольких операциях округления округление до ближайшего уменьшает ошибку округления, вызванную последовательным округлением средних значений в одном направлении. В некоторых случаях эта ошибка округления может быть значительной.
В следующем примере показано смещение, которое может быть результатом последовательного округления средних значений в одном направлении. В этом примере вычисляется истинное среднее массива значений Decimal , а затем вычисляется среднее, когда значения в массиве округляются с помощью двух соглашений. В этом примере истинное среднее и среднее значение, которое результаты при округлении до ближайшего совпадают. Однако среднее значение, которое результаты при округлении от нуля отличаются на 0,05 (или на 3,6 %) от истинного среднего.
decimal[] values = < 1.15m, 1.25m, 1.35m, 1.45m, 1.55m, 1.65m >; decimal sum = 0; // Calculate true mean. foreach (var value in values) sum += value; Console.WriteLine("True mean: ", sum / values.Length); // Calculate mean with rounding away from zero. sum = 0; foreach (var value in values) sum += Math.Round(value, 1, MidpointRounding.AwayFromZero); Console.WriteLine("AwayFromZero: ", sum / values.Length); // Calculate mean with rounding to nearest. sum = 0; foreach (var value in values) sum += Math.Round(value, 1, MidpointRounding.ToEven); Console.WriteLine("ToEven: ", sum / values.Length); // The example displays the following output: // True mean: 1.40 // AwayFromZero: 1.45 // ToEven: 1.40
open System let values = [| 1.15m; 1.25m; 1.35m; 1.45m; 1.55m; 1.65m |] let mutable sum = 0m // Calculate true mean. for value in values do sum " // Calculate mean with rounding away from zero. sum " // Calculate mean with rounding to nearest. sum " // The example displays the following output: // True mean: 1.40 // AwayFromZero: 1.45 // ToEven: 1.40
Dim values() As Decimal = Dim sum As Decimal ' Calculate true mean. For Each value In values sum += value Next Console.WriteLine("True mean: ", sum / values.Length) ' Calculate mean with rounding away from zero. sum = 0 For Each value In values sum += Math.Round(value, 1, MidpointRounding.AwayFromZero) Next Console.WriteLine("AwayFromZero: ", sum / values.Length) ' Calculate mean with rounding to nearest. sum = 0 For Each value In values sum += Math.Round(value, 1, MidpointRounding.ToEven) Next Console.WriteLine("ToEven: ", sum / values.Length) ' The example displays the following output: ' True mean: 1.40 ' AwayFromZero: 1.45 ' ToEven: 1.40
По умолчанию Round метод использует цикл для ближайшего четного соглашения. В следующей таблице перечислены перегрузки Round метода и соглашения о округлениях, которые используются каждым из них.
| Перегрузка | Соглашение о округлениях |
|---|---|
| Round(Decimal) | ToEven |
| Round(Double) | ToEven |
| Round(Decimal, Int32) | ToEven |
| Round(Double, Int32) | ToEven |
| Round(Decimal, MidpointRounding) | Определяется параметром mode . |
| Round(Double, MidpointRounding) | Определяется параметром mode |
| Round(Decimal, Int32, MidpointRounding) | Определяется параметром mode |
| Round(Double, Int32, MidpointRounding) | Определяется параметром mode |
Округление и точность
Чтобы определить, включает ли операция округления среднее значение, Round метод умножает исходное значение для округления на 10 n , где n — требуемое число дробных цифр в возвращаемом значении, а затем определяет, является ли оставшаяся дробная часть значения больше или равна 0,5. Это небольшой вариант проверки на равенство, и, как описано в разделе «Тестирование на равенство» справочного Double раздела, тесты на равенство со значениями с плавающей запятой являются проблематичными из-за проблем формата с плавающей запятой с двоичным представлением и точностью. Это означает, что любая дробная часть числа, которая немного меньше 0,5 (из-за потери точности), не будет округлена вверх.
В следующем примере показана эта проблема. Он многократно добавляет .1 к 11,0 и округляет результат до ближайшего целого числа. Значение 11.5 должно округлиться до 12, используя одно из соглашений об округлениях в середине ( ToEven или AwayFromZero ). Однако, как показано в выходных данных из примера, это не так. В примере используется строка стандартного числового формата «R» для отображения полной точности значения с плавающей запятой и показывается, что округляемое значение потеряло точность во время повторяющихся сложений, а его значение на самом деле равно 11,499999999999999998. Поскольку значение .499999999999998 меньше 0,5, соглашения о скруглениях в середине не вступают в игру, а значение округляется вниз. Как показано в примере, эта проблема не возникает, если присвоить переменной Double значение константы 11,5.
public static void Example() < Console.WriteLine(" \n", "Value", "Full Precision", "ToEven", "AwayFromZero"); double value = 11.1; for (int ctr = 0; ctr private static double RoundValueAndAdd(double value) < Console.WriteLine(" ", value, Math.Round(value, MidpointRounding.ToEven), Math.Round(value, MidpointRounding.AwayFromZero)); return value + .1; > // The example displays the following output: // Value Full Precision ToEven AwayFromZero // // 11.1 11.1 11 11 // 11.2 11.2 11 11 // 11.3 11.299999999999999 11 11 // 11.4 11.399999999999999 11 11 // 11.5 11.499999999999998 11 11 // 11.6 11.599999999999998 12 12 // // 11.5 11.5 12 12
open System let roundValueAndAdd (value: double) = printfn $" " value + 0.1 printfn "%5s %20s %12s %15s\n" "Value" "Full Precision" "ToEven" "AwayFromZero" let mutable value = 11.1 for _ = 0 to 5 do value ignore // The example displays the following output: // Value Full Precision ToEven AwayFromZero // // 11.1 11.1 11 11 // 11.2 11.2 11 11 // 11.3 11.299999999999999 11 11 // 11.4 11.399999999999999 11 11 // 11.5 11.499999999999998 11 11 // 11.6 11.599999999999998 12 12 // // 11.5 11.5 12 12
Public Sub Example() Dim value As Double = 11.1 Console.WriteLine(" ", "Value", "Full Precision", "ToEven", "AwayFromZero") Console.WriteLine() For ctr As Integer = 0 To 5 value = RoundValueAndAdd(value) Next Console.WriteLine() value = 11.5 RoundValueAndAdd(value) End Sub Private Function RoundValueAndAdd(value As Double) As Double Console.WriteLine(" ", value, Math.Round(value, MidpointRounding.ToEven), Math.Round(value, MidpointRounding.AwayFromZero)) Return value + 0.1 End Function ' The example displays the following output: ' Value Full Precision ToEven AwayFromZero ' ' 11.1 11.1 11 11 ' 11.2 11.2 11 11 ' 11.3 11.299999999999999 11 11 ' 11.4 11.399999999999999 11 11 ' 11.5 11.499999999999998 11 11 ' 11.6 11.599999999999998 12 12 ' ' 11.5 11.5 12 12
Проблемы с точностью округления средних точек, скорее всего, возникают в следующих условиях:
- Если дробное значение не может быть выражено точно в двоичном формате типа с плавающей запятой.
- Если округляемое значение вычисляется на основе одной или нескольких операций с плавающей запятой.
- Если округляемое значение равно , Single а не или DoubleDecimal. Дополнительные сведения см. в следующем разделе Округление и значения с плавающей запятой с одной точностью.
В случаях, когда отсутствие точности в операциях округления является проблематичным, можно сделать следующее:
- Если операция округления вызывает перегрузку, округляющую Double значение, можно изменить DoubleDecimal на значение и вызвать перегрузку, округляющую Decimal значение. Хотя тип Decimal данных также имеет проблемы с представлением и потерей точности, эти проблемы встречаются гораздо реже.
- Определите пользовательский алгоритм округления, который выполняет «почти равный» тест, чтобы определить, допустимо ли округляемое значение близко к средней точке. В следующем примере определяется RoundApproximate метод, который проверяет, достаточно ли дробное значение близко к средней точке для округления в середине. Как показано в выходных данных из примера, он исправляет проблему округления, показанную в предыдущем примере.
public static void Example() < Console.WriteLine(" \n", "Value", "Full Precision", "ToEven", "AwayFromZero"); double value = 11.1; for (int ctr = 0; ctr private static double RoundValueAndAdd(double value) < const double tolerance = 8e-14; Console.WriteLine(" ", value, RoundApproximate(value, 0, tolerance, MidpointRounding.ToEven), RoundApproximate(value, 0, tolerance, MidpointRounding.AwayFromZero)); return value + .1; > private static double RoundApproximate(double dbl, int digits, double margin, MidpointRounding mode) < double fraction = dbl * Math.Pow(10, digits); double value = Math.Truncate(fraction); fraction = fraction - value; if (fraction == 0) return dbl; double tolerance = margin * dbl; // Determine whether this is a midpoint value. if ((fraction >= .5 - tolerance) & (fraction // Any remaining fractional value greater than .5 is not a midpoint value. if (fraction > .5) return (value + 1) / Math.Pow(10, digits); else return value / Math.Pow(10, digits); > // The example displays the following output: // Value Full Precision ToEven AwayFromZero // // 11.1 11.1 11 11 // 11.2 11.2 11 11 // 11.3 11.299999999999999 11 11 // 11.4 11.399999999999999 11 11 // 11.5 11.499999999999998 12 12 // 11.6 11.599999999999998 12 12 // // 11.5 11.5 12 12
open System let roundApproximate dbl digits margin mode = let fraction = dbl * Math.Pow(10, digits) let value = Math.Truncate fraction let fraction = fraction - value if fraction = 0 then dbl else let tolerance = margin * dbl // Determine whether this is a midpoint value. if (fraction >= 0.5 - tolerance) && (fraction 0 then (value + 1.) / Math.Pow(10, digits) else value / Math.Pow(10, digits) // Any remaining fractional value greater than .5 is not a midpoint value. elif fraction > 0.5 then (value + 1.) / Math.Pow(10, digits) else value / Math.Pow(10, digits) let roundValueAndAdd value = let tolerance = 8e-14 let round = roundApproximate value 0 tolerance printfn $" " value + 0.1 printfn "%5s %20s %12s %15s\n" "Value" "Full Precision" "ToEven" "AwayFromZero" let mutable value = 11.1 for _ = 0 to 5 do value ignore // The example displays the following output: // Value Full Precision ToEven AwayFromZero // // 11.1 11.1 11 11 // 11.2 11.2 11 11 // 11.3 11.299999999999999 11 11 // 11.4 11.399999999999999 11 11 // 11.5 11.499999999999998 12 12 // 11.6 11.599999999999998 12 12 // // 11.5 11.5 12 12
Public Sub Example() Dim value As Double = 11.1 Console.WriteLine(" \n", "Value", "Full Precision", "ToEven", "AwayFromZero") For ctr As Integer = 0 To 5 value = RoundValueAndAdd(value) Next Console.WriteLine() value = 11.5 RoundValueAndAdd(value) End Sub Private Function RoundValueAndAdd(value As Double) As Double Const tolerance As Double = 0.00000000000008 Console.WriteLine(" ", value, RoundApproximate(value, 0, tolerance, MidpointRounding.ToEven), RoundApproximate(value, 0, tolerance, MidpointRounding.AwayFromZero)) Return value + 0.1 End Function Private Function RoundApproximate(dbl As Double, digits As Integer, margin As Double, mode As MidpointRounding) As Double Dim fraction As Double = dbl * Math.Pow(10, digits) Dim value As Double = Math.Truncate(fraction) fraction = fraction - value If fraction = 0 Then Return dbl Dim tolerance As Double = margin * dbl ' Determine whether this is a midpoint value. If (fraction >= 0.5 - tolerance) And (fraction 0 Then Return (value + 1) / Math.Pow(10, digits) Else Return value / Math.Pow(10, digits) End If End If End If ' Any remaining fractional value greater than .5 is not a midpoint value. If fraction > 0.5 Then Return (value + 1) / Math.Pow(10, digits) Else Return value / Math.Pow(10, digits) End If End Function ' The example displays the following output: ' Value Full Precision ToEven AwayFromZero ' ' 11.1 11.1 11 11 ' 11.2 11.2 11 11 ' 11.3 11.299999999999999 11 11 ' 11.4 11.399999999999999 11 11 ' 11.5 11.499999999999998 12 12 ' 11.6 11.599999999999998 12 12 ' ' 11.5 11.5 12 12
Округление и одноточие значений с плавающей запятой
Метод Round включает перегрузки, принимаюющие аргументы типа Decimal и Double. Нет методов, округляющих значения типа Single. Если передать Single значение одной из перегрузок Round метода, оно приводится (в C#) или преобразуется (в Visual Basic) Doubleв и вызывается соответствующая Round перегрузка с параметром Double . Хотя это расширяющееся преобразование, оно часто связано с потерей точности, как показано в следующем примере. Single Если значение 16,325 передается методу Round и округляется до двух десятичных разрядов с помощью округления до ближайшего соглашения, результат будет равен 16,33, а не ожидаемым результатом 16,32.
Single value = 16.325f; Console.WriteLine("Widening Conversion of (type ) to (type ): ", value, value.GetType().Name, (double)value, ((double)(value)).GetType().Name); Console.WriteLine(Math.Round(value, 2)); Console.WriteLine(Math.Round(value, 2, MidpointRounding.AwayFromZero)); Console.WriteLine(); Decimal decValue = (decimal)value; Console.WriteLine("Cast of (type ) to (type ): ", value, value.GetType().Name, decValue, decValue.GetType().Name); Console.WriteLine(Math.Round(decValue, 2)); Console.WriteLine(Math.Round(decValue, 2, MidpointRounding.AwayFromZero)); // The example displays the following output: // Widening Conversion of 16.325 (type Single) to 16.325000762939453 (type Double): // 16.33 // 16.33 // // Cast of 16.325 (type Single) to 16.325 (type Decimal): // 16.32 // 16.33
// In F#, 'float', 'float64', and 'double' are aliases for System.Double. // 'float32' and 'single' are aliases for System.Single open System let value = 16.325f printfn $"Widening Conversion of (type ) to (type <(double value).GetType().Name>): " printfn $"" printfn $"" printfn "" let decValue = decimal value printfn $"Cast of (type ) to (type ): " printfn $"" printfn $"" // The example displays the following output: // Widening Conversion of 16.325 (type Single) to 16.325000762939453 (type Double): // 16.33 // 16.33 // // Cast of 16.325 (type Single) to 16.325 (type Decimal): // 16.32 // 16.33
Dim value As Single = 16.325 Console.WriteLine("Widening Conversion of (type ) to (type ): ", value, value.GetType().Name, CDbl(value), CDbl(value).GetType().Name) Console.WriteLine(Math.Round(value, 2)) Console.WriteLine(Math.Round(value, 2, MidpointRounding.AwayFromZero)) Console.WriteLine() Dim decValue As Decimal = CDec(value) Console.WriteLine("Cast of (type ) to (type ): ", value, value.GetType().Name, decValue, decValue.GetType().Name) Console.WriteLine(Math.Round(decValue, 2)) Console.WriteLine(Math.Round(decValue, 2, MidpointRounding.AwayFromZero)) Console.WriteLine() ' The example displays the following output: ' Widening Conversion of 16.325 (type Single) to 16.325000762939453 (type Double): ' 16.33 ' 16.33 ' ' Cast of 16.325 (type Single) to 16.325 (type Decimal): ' 16.32 ' 16.33
Этот непредвиденный результат связан с потерей точности при преобразовании значения Doubleв Single . Так как полученное Double значение 16,325000762939453 не является средней точкой и больше 16,325, оно всегда округляется вверх.
Во многих случаях, как показано в примере, потерю точности можно свести к минимуму или устранить путем приведения или преобразования значения в SingleDecimal. Обратите внимание, что, поскольку это сужающее преобразование, требуется использовать оператор приведения или вызвать метод преобразования.
Round(Double, Int32, MidpointRounding)
Округляет значение двойной точности с плавающей запятой до указанного числа дробных цифр, используя указанное соглашение о округлении.
public: static double Round(double value, int digits, MidpointRounding mode);
public static double Round (double value, int digits, MidpointRounding mode);
static member Round : double * int * MidpointRounding -> double
Public Shared Function Round (value As Double, digits As Integer, mode As MidpointRounding) As Double
Параметры
Округляемое число двойной точности с плавающей запятой.
Количество дробных разрядов в возвращаемом значении.
Одно из значений перечисления, указывающее используемую стратегию округления.
Возвращаемое значение
Число, которое содержит digits дробные цифры, value округленные до. Если value имеет меньшее количество цифр дробной части, чем digits , то value возвращается без изменений.
Исключения
Параметр digits имеет значение меньше 0 или больше 15.
mode не является допустимым значением MidpointRounding.
Комментарии
Значение аргумента digits может варьироваться от 0 до 15. Максимальное число целых и дробных цифр, поддерживаемых типом Double , равно 15.
Сведения о округлениях чисел со значениями в середине см. в статье Midpoint values and rounding conventions (Соглашения о округлениях по середине и соглашениям о округлениях).
При округлении средних значений алгоритм округления выполняет проверку равенства. Из-за проблем с двоичным представлением и точностью формата с плавающей запятой значение, возвращаемое этим методом, может быть непредвиденным. Дополнительные сведения см. в разделе Округление и точность.
Если аргумент имеет Double.NaNзначение value , метод возвращает Double.NaN. Если value имеет значение Double.PositiveInfinity или Double.NegativeInfinity, метод возвращает Double.PositiveInfinity или Double.NegativeInfinityсоответственно.
Пример
В следующем примере показано, как использовать Round(Double, Int32, MidpointRounding) метод с перечислением MidpointRounding .
// Round a positive and a negative value using the default. double result = Math.Round(3.45, 1); Console.WriteLine($" = Math.Round(, 1)"); result = Math.Round(-3.45, 1); Console.WriteLine($" = Math.Round(, 1)\n"); // Round a positive value using a MidpointRounding value. result = Math.Round(3.45, 1, MidpointRounding.ToEven); Console.WriteLine($" = Math.Round(, 1, MidpointRounding.ToEven)"); result = Math.Round(3.45, 1, MidpointRounding.AwayFromZero); Console.WriteLine($" = Math.Round(, 1, MidpointRounding.AwayFromZero)"); result = Math.Round(3.47, 1, MidpointRounding.ToZero); Console.WriteLine($" = Math.Round(, 1, MidpointRounding.ToZero)\n"); // Round a negative value using a MidpointRounding value. result = Math.Round(-3.45, 1, MidpointRounding.ToEven); Console.WriteLine($" = Math.Round(, 1, MidpointRounding.ToEven)"); result = Math.Round(-3.45, 1, MidpointRounding.AwayFromZero); Console.WriteLine($" = Math.Round(, 1, MidpointRounding.AwayFromZero)"); result = Math.Round(-3.47, 1, MidpointRounding.ToZero); Console.WriteLine($" = Math.Round(, 1, MidpointRounding.ToZero)\n"); // The example displays the following output: // 3.4 = Math.Round( 3.45, 1) // -3.4 = Math.Round(-3.45, 1) // 3.4 = Math.Round(3.45, 1, MidpointRounding.ToEven) // 3.5 = Math.Round(3.45, 1, MidpointRounding.AwayFromZero) // 3.4 = Math.Round(3.47, 1, MidpointRounding.ToZero) // -3.4 = Math.Round(-3.45, 1, MidpointRounding.ToEven) // -3.5 = Math.Round(-3.45, 1, MidpointRounding.AwayFromZero) // -3.4 = Math.Round(-3.47, 1, MidpointRounding.ToZero)
// Round a positive and a negative value using the default. let result = Math.Round(3.45, 1) printfn $" = Math.Round(, 1)" let result = Math.Round(-3.45, 1) printfn $" = Math.Round(, 1)\n" // Round a positive value using a MidpointRounding value. let result = Math.Round(3.45, 1, MidpointRounding.ToEven) printfn $" = Math.Round(, 1, MidpointRounding.ToEven)" let result = Math.Round(3.45, 1, MidpointRounding.AwayFromZero) printfn $" = Math.Round(, 1, MidpointRounding.AwayFromZero)" let result = Math.Round(3.47, 1, MidpointRounding.ToZero) printfn $" = Math.Round(, 1, MidpointRounding.ToZero)\n" // Round a negative value using a MidpointRounding value. let result = Math.Round(-3.45, 1, MidpointRounding.ToEven) printfn $" = Math.Round(, 1, MidpointRounding.ToEven)" let result = Math.Round(-3.45, 1, MidpointRounding.AwayFromZero) printfn $" = Math.Round(, 1, MidpointRounding.AwayFromZero)" let result = Math.Round(-3.47, 1, MidpointRounding.ToZero) printfn $" = Math.Round(, 1, MidpointRounding.ToZero)\n" // The example displays the following output: // 3.4 = Math.Round( 3.45, 1) // -3.4 = Math.Round(-3.45, 1) // 3.4 = Math.Round(3.45, 1, MidpointRounding.ToEven) // 3.5 = Math.Round(3.45, 1, MidpointRounding.AwayFromZero) // 3.4 = Math.Round(3.47, 1, MidpointRounding.ToZero) // -3.4 = Math.Round(-3.45, 1, MidpointRounding.ToEven) // -3.5 = Math.Round(-3.45, 1, MidpointRounding.AwayFromZero) // -3.4 = Math.Round(-3.47, 1, MidpointRounding.ToZero)
Dim posValue As Double = 3.45 Dim negValue As Double = -3.45 ' Round a positive and a negative value using the default. Dim result As Double = Math.Round(posValue, 1) Console.WriteLine(" = Math.Round(, 1)", result, posValue) result = Math.Round(negValue, 1) Console.WriteLine(" = Math.Round(, 1)", result, negValue) Console.WriteLine() ' Round a positive value using a MidpointRounding value. result = Math.Round(posValue, 1, MidpointRounding.ToEven) Console.WriteLine(" = Math.Round(, 1, MidpointRounding.ToEven)", result, posValue) result = Math.Round(posValue, 1, MidpointRounding.AwayFromZero) Console.WriteLine(" = Math.Round(, 1, MidpointRounding.AwayFromZero)", result, posValue) Console.WriteLine() ' Round a positive value using a MidpointRounding value. result = Math.Round(negValue, 1, MidpointRounding.ToEven) Console.WriteLine(" = Math.Round(, 1, MidpointRounding.ToEven)", result, negValue) result = Math.Round(negValue, 1, MidpointRounding.AwayFromZero) Console.WriteLine(" = Math.Round(, 1, MidpointRounding.AwayFromZero)", result, negValue) Console.WriteLine() 'This code example produces the following results: ' 3.4 = Math.Round( 3.45, 1) ' -3.4 = Math.Round(-3.45, 1) ' 3.4 = Math.Round( 3.45, 1, MidpointRounding.ToEven) ' 3.5 = Math.Round( 3.45, 1, MidpointRounding.AwayFromZero) ' -3.4 = Math.Round(-3.45, 1, MidpointRounding.ToEven) ' -3.5 = Math.Round(-3.45, 1, MidpointRounding.AwayFromZero)
Примечания для тех, кто вызывает этот метод
Из-за потери точности, которая может быть вызвана представлением десятичных значений в виде чисел с плавающей запятой или выполнением арифметических операций со значениями с плавающей запятой, в некоторых случаях Round(Double, Int32, MidpointRounding) метод может не округлить значения середины, как указано в параметре mode . Это показано в следующем примере, где 2,135 округляется до 2,13 вместо 2,14. Это происходит потому, что внутри метода умножается value на 10 цифр , а операция умножения в этом случае страдает от потери точности.
double[] values = < 2.125, 2.135, 2.145, 3.125, 3.135, 3.145 >; foreach (double value in values) Console.WriteLine(" --> ", value, Math.Round(value, 2, MidpointRounding.AwayFromZero)); // The example displays the following output: // 2.125 --> 2.13 // 2.135 --> 2.13 // 2.145 --> 2.15 // 3.125 --> 3.13 // 3.135 --> 3.14 // 3.145 --> 3.15
open System let values = [| 2.125; 2.135; 2.145; 3.125; 3.135; 3.145 |] for value in values do printfn $" --> " // The example displays the following output: // 2.125 --> 2.13 // 2.135 --> 2.13 // 2.145 --> 2.15 // 3.125 --> 3.13 // 3.135 --> 3.14 // 3.145 --> 3.15
Module Example Public Sub Main() Dim values() As Double = < 2.125, 2.135, 2.145, 3.125, 3.135, 3.145 >For Each value As Double In values Console.WriteLine(" --> ", value, Math.Round(value, 2, MidpointRounding.AwayFromZero)) Next End Sub End Module ' The example displays the following output: ' 2.125 --> 2.13 ' 2.135 --> 2.13 ' 2.145 --> 2.15 ' 3.125 --> 3.13 ' 3.135 --> 3.14 ' 3.145 --> 3.15
См. также раздел
- Ceiling(Double)
- Floor(Double)
- Round(Decimal, Int32, MidpointRounding)