Как округлить число до n знаков после запятой в java
Содержание:
- Как подключить джойстик к компьютеру Windows 10? Рекомендации по настройке
- Overview
- 2. Decimal Numbers in Java
- 3. Formatting a Decimal Number
- 4. Rounding Doubles With BigDecimal
- 5. Rounding Doubles With DoubleRounder
- 6. Math.round() Method
- 7. Conclusion
- 1 Тригонометрические функции в Java
- Методы класса чисел
- Работающие реализации на Go
- Java Integer Math
- Способы записи числа
- Java Math.round on Array example
- 1 Округление вещественных чисел
- Способы округления чисел
- Math.round Java examples
- Literals
- Округление при работе с числами ограниченной точности
- 5 Интересный факт о strictfp
- Где делать округление: размышления о точности
- Заключение
Как подключить джойстик к компьютеру Windows 10? Рекомендации по настройке
Overview
In this short article, we’re going to look at how to round a number to n decimal places in Java.
2. Decimal Numbers in Java
Java provides two primitive types that can be used for storing decimal numbers: float and double. Double is the type used by default:
However, both types should never be used for precise values, such as currencies. For that, and also for rounding, we can use the BigDecimal class.
3. Formatting a Decimal Number
If we just want to print a decimal number with n digits after decimal point, we can simply format the output String:
Alternatively, we can format the value with the DecimalFormat class:
DecimalFormat allows us to explicitly set rounding behavior, giving more control of the output than String.format() used above.
4. Rounding Doubles With BigDecimal
To round doubles to n decimal places, we can write a helper method:
There is one important thing to notice in this solution – when constructing BigDecimal; we must always use BigDecimal(String) constructor. This prevents issues with representing inexact values.
We can achieve the same by using the Apache Commons Math library:
The latest version can be found .
Once the library is added to the project, we can use the Precision.round() method, which takes two arguments – value and scale:
By default, it is using the same HALF_UP rounding method as our helper method. Therefore, the results should be the same.
Note that we can change rounding behavior by passing the desired rounding method as a third parameter.
5. Rounding Doubles With DoubleRounder
DoubleRounder is a utility in the decimal4j library. It provides a fast and garbage-free method for rounding doubles from 0 to 18 decimal points.
We can get the library (the latest version can be found ) by adding the dependency to the pom.xml:
Now, we can simply use:
However, DoubleRounder fails in a few scenarios, for example:
6. Math.round() Method
Another way of rounding numbers is to use Math.Round() Method.
In this case, we can control n number of decimal places by multiplying and dividing by 10^n:
This method is not recommended as it’s truncating the value. In many cases values are rounded incorrectly:
And so, this method is listed here for learning purposes only.
7. Conclusion
In this quick tutorial, we covered different techniques for rounding numbers to n decimal places.
We can simply format the output without changing the value, or we can round the variable by using a helper method. We’ve also covered a few libraries that deal with this problem.
The code used during the discussion can be found over on GitHub.
1 Тригонометрические функции в Java
Ранее, когда мы изучали округление чисел, мы познакомились с классом и некоторыми его методами. Сейчас мы рассмотрим этот класс более подробно.
В классе , на что как бы намекает это название, собраны самые часто употребляемые программистами математические функции. Вот самые интересные из них:
Метод | Описание |
---|---|
Возвращает синус угла , заданного в радианах | |
Возвращает косинус угла , заданного в радианах | |
Возвращает тангенс угла , заданного в радианах | |
Возвращает арксинус | |
Возвращает арккосинус | |
Возвращает арктангенс | |
Возвращает гиперболический синус | |
Возвращает гиперболический косинус | |
Возвращает гиперболический тангенс |
Функции , и принимают угол заданный в радианах. Чтобы преобразовать углы из градусов в радианы и обратно, в классе есть две специальные функции:
Метод | Описание |
---|---|
Преобразует угол из градусов в радианы | |
Преобразует угол из радиан в градусы |
В классе , кстати, есть не только функции, но еще и две переменные-константы (статические поля класса):
Константа | Описание |
---|---|
Число «Пи» равное | |
Число «Е» равное |
Все эти функции могут быть вам очень полезны, если вы решите писать свои игры, работать с графикой, ну или просто посчитать длину пути по карте.
Например, если вы хотите вычислить , вот как можно это сделать:
Пример использования:
Методы класса чисел
Список методов всех подклассов класса чисел в Java:
№ | Метод с описанием |
1 |
xxxValue() Преобразует значение целочисленного объекта в ххх тип данных и возвращает его. |
2 |
compareTo() Сравнивает целочисленный объект с аргументом. |
3 |
equals() Определяет, является ли целочисленный объект равным аргументу. |
4 |
valueOf() Возвращает целочисленный объект, держа указанное значение. |
5 |
toString() Возвращает строковый объект (String), представляющий указанное значение int или целочисленный объект. |
6 |
parseInt() Метод используется для получения примитивного типа данных определенной строки. |
7 |
abs() Возвращает абсолютное значение аргумента. |
8 |
ceil() Возвращает наименьшее (ближайшее к отрицательной бесконечности) double значение, которое больше или равно аргументу и равное математическому целому числу. |
9 |
floor() Возвращает наибольшее (ближайшее к положительной бесконечности) double значение, которое меньше или равно аргумента и равно математическому целому числу. |
10 |
rint() Возвращает double значение, которое ближе всего по значению аргумента и равно математическому целому числу. |
11 |
round() Возвращает ближайшее long или int к аргументу по правилам округления. |
12 |
min() Возвращает меньшее из двух аргументов. |
13 |
max() Возвращает большее из двух аргументов. |
14 |
exp() Возвращает число е Эйлера, возведенную в степень double значения. |
15 |
log() Возвращает натуральный логарифм (по основанию е) с double значением. |
16 |
pow() Возвращает значение первого аргумента, возведенное в степень второго аргумента. |
17 |
sqrt() Возвращает правильно округленный положительный квадратный корень из double значения. |
18 |
sin() Возвращает синус указанного double значения. |
19 |
cos() Возвращает косинус указанного double значения. |
20 |
tan() Возвращает тангенс указанного double значения. |
21 |
asin() Возвращает арксинус указанного double значения. |
22 |
acos() Возвращает арккосинус указанного double значения. |
23 |
atan() Возвращает арктангенс указанного double значения. |
24 |
atan2() Возвращает угол тета от преобразования прямоугольных координат (x, y) в полярных координатах (г, тета). |
25 |
toDegrees() Преобразует угол, измеренный в радианах в примерно эквивалентном угол, измеренный в градусах. |
26 |
toRadians() Преобразует угол, измеренный в градусах, в приблизительно эквивалентный угол, измеренный в радианах. |
27 |
random() Возвращает double значение с положительным знаком, больше чем или равно 0.0 и меньше чем 1.0 . |
Поделитесь:
Работающие реализации на Go
Round(), используемая в Postgres
Выше я уже упоминал, что в Postgres содержится код функции Round() на C, который работает для всех тестируемых значений. В CockroachDB мы , без комментариев он выглядит следующим образом:
Давайте разберёмся, как он работает. Первые шесть строк обрабатывают особые случаи. Далее мы выбираем roundFn из Ceil и Floor в зависимости от того, положительное число или отрицательное. Далее начинается самое интересное:
Этим кодом мы сдвигаем x ближе к нулю.
Далее мы проверяем, не стал ли x в точности нулём и не поменялся ли у него знак. Это означает что исходное число <= 0,5, в этом случае мы возвращаем ноль с нужным знаком.
Эта проверка нужна для очень больших чисел, для которых x-0,5 == x-1,0, в этих случаях мы можем вернуть число неизменённым.
Далее мы округляем число с помощью Floor() или Ceil() и возвращаем это значение, если оно отличается от x, что может случиться, только если дробная часть входного значения не равна в точности 0,5, так как выше мы вычли 0,5 из него.
Теперь мы знаем, что дробная часть равна 0,5, поэтому нам нужно округлить до ближайшего чётного числа (реализация Round() в Postgres в этом месте отличается от приведённых выше вариантов). Комментарий в коде лучше это описывает:
Чтобы сохранить оригинальное поведение, этот код можно заменить на следующий:
github.com/montanaflynn/stats
Ещё одна работающая реализация содержится в пакете github.com/montanaflynn/stats. Без комментариев она выглядит следующим образом:
Ключевое отличие от предыдущих решений заключается в использовании функции Modf(), которая корректно разделяет целую и дробную части чисел.
Java Integer Math
Математические операции, выполняемые с целочисленными типами Java (byte, short, int и long), ведут себя немного иначе, чем обычные математические операции. Поскольку целочисленные типы не могут содержать дроби, в каждом вычислении с одним или несколькими целочисленными типами все дроби в результате обрезаются. Посмотрите на это математическое выражение:
int result = 100 / 8;
Результат этого деления будет 12,5, но так как два числа являются целыми числами, фракция .5 обрезается. Результат, следовательно, всего 12.
Округление также происходит в подрезультатах больших вычислений.
С плавающей точкой Math
Java содержит два типа данных с плавающей точкой: float и double. Они могут содержать дроби в числах. Если нужны дробные выражения в математических выражениях, вы должны использовать один из этих типов данных. Вот пример математического выражения с плавающей точкой:
double result = 100 / 8;
Несмотря на то, что переменная результата теперь имеет тип с плавающей запятой (double), конечный результат по-прежнему равен 12 вместо 12,5. Причина в том, что оба значения в математическом выражении (100 и 8) оба являются целыми числами. Таким образом, результат деления одного на другое сначала преобразуется в целое число (12), а затем присваивается переменной результата.
Чтобы избежать округления вычислений, необходимо убедиться, что все типы данных, включенные в математическое выражение, являются типами с плавающей запятой. Например, вы могли бы сначала присвоить значения переменным с плавающей запятой следующим образом:
double no1 = 100; double no2 = 8; double result = no1 / no2;
Теперь переменная результата будет иметь значение 12,5.
В Java есть способ заставить все числа в расчете быть переменными с плавающей точкой. Вы ставите числа с большой буквы F или D. Вот пример:
double result = 100D / 8D;
Обратите внимание на прописные буквы D после каждого числа. Этот верхний регистр D говорит Java, что эти числа должны интерпретироваться как числа с плавающей запятой, и, таким образом, деление должно быть делением с плавающей запятой, которое сохраняет дроби вместо их обрезания
На самом деле вы также можете сделать число длинным, добавив суффикс числа к верхнему регистру L, но long по-прежнему является целочисленным типом, поэтому он не будет сохранять дробные части в вычислениях.
Точность с плавающей точкой
Типы данных с плавающей точкой не являются точными на 100%. Вы можете столкнуться с ситуациями, когда числа со многими дробями не складываются с ожидаемым числом. Если вычисление с плавающей запятой приводит к числу с большим количеством дробей, чем может обработать число с плавающей запятой или двойное число, дроби могут быть обрезаны. Конечно, заданная точность может быть более чем достаточной для многих типов вычислений, но имейте в виду, что дроби могут фактически быть отсечены.
Посмотрите:
double resultDbl3 = 0D; System.out.println("resultDbl3 = " + resultDbl3); for(int i=0; i<100; i++){ resultDbl3 += 0.01D; } System.out.println("resultDbl3 = " + resultDbl3);
Вывод выводится при выполнении этого кода с Java 8:
resultDbl3 = 0.0 resultDbl3 = 1.0000000000000007
Первый оператор System.out.println() правильно печатает значение 0.0, которое является начальным значением переменной resultDbl3.
Однако второй оператор System.out.println() выводит несколько странный результат. Добавление значения 0,01 к 0 всего 100 раз должно привести к значению 1,0, верно? Но каким-то образом окончательный результат 1.0000000000000007. Как видите, что-то не так во фракциях.
Обычно неточность с плавающей запятой незначительна, но все же важно знать об этом
Способы записи числа
Представьте, что нам надо записать число 1 миллиард. Самый очевидный путь:
Но в реальной жизни мы обычно опускаем запись множества нулей, так как можно легко ошибиться. Укороченная запись может выглядеть как или для 7 миллиардов 300 миллионов. Такой принцип работает для всех больших чисел.
В JavaScript можно использовать букву , чтобы укоротить запись числа. Она добавляется к числу и заменяет указанное количество нулей:
Другими словами, производит операцию умножения числа на 1 с указанным количеством нулей.
Сейчас давайте запишем что-нибудь очень маленькое. К примеру, 1 микросекунду (одна миллионная секунды):
Записать микросекунду в укороченном виде нам поможет .
Если мы подсчитаем количество нулей , их будет 6. Естественно, верная запись .
Другими словами, отрицательное число после подразумевает деление на 1 с указанным количеством нулей:
Шестнадцатеричные числа широко используются в JavaScript для представления цветов, кодировки символов и многого другого. Естественно, есть короткий стиль записи: , после которого указывается число.
Например:
Не так часто используются двоичные и восьмеричные числа, но они также поддерживаются для двоичных и для восьмеричных:
Есть только 3 системы счисления с такой поддержкой. Для других систем счисления мы рекомендуем использовать функцию (рассмотрим позже в этой главе).
Java Math.round on Array example
In this Java program, we find the rounded values of bulk data. Here, we are going to declare an array of double type and find the closest (rounded) values of array elements using java math.round.
OUTPUT
ANALYSIS
Within this Java round example, we declared an Array of double type and assigned some random values.
Next, We used the Java For Loop to iterate the Array. Within the For Loop, we initialized the i value as 0.
Next, compiler will check for the condition (i < myArray.length). As along the condition is True statement inside the for loop executed.
TIP: myArray.length finds the length of the Java array.
The following statements will print the output. If you observe the code snippet, we used the round Function directly inside System.out.format statement.
Here, the compiler will call the Java Math.round method ( static long round(double number) ) to find the corresponding closet (rounded) values.
NOTE: To find the closet (rounded) value of a single item, then use: Math.round(myArray)
1 Округление вещественных чисел
Как мы уже разбирали, при присваивании переменной типа вещественного числа оно всегда округляется вниз до целого — его дробная часть просто отбрасывается.
А ведь легко можно представить ситуацию, когда дробное число нужно округлить просто до ближайшего целого или вообще вверх. Что делать в этой ситуации?
Для этого и для многих похожих случаев в Java есть класс , у которого есть методы , , .
Метод
Метод округляет число до ближайшего целого:
Но, как говорится, есть нюанс: результат работы этого метода — целочисленный тип (не ). Вещественные числа ведь могут быть очень большими, поэтому разработчики Java решили использовать самый большой целочисленный тип, который есть в Java — .
Поэтому чтобы присвоить результат в переменную типа , программист должен явно указать компилятору, что он согласен с возможной потерей данных (вдруг число не поместится в тип ).
Примеры:
Команда | Результат |
---|---|
Метод
Метод округляет число до целого вверх, примеры:
Команда | Результат |
---|---|
Метод
Метод округляет число до целого вниз, примеры:
Команда | Результат |
---|---|
Хотя, для округления числа до целого вниз, будет проще использовать просто оператор приведения типа — :
Команда | Результат |
---|---|
Если вам сложно запомнить эти команды, вам поможет небольшой урок английского:
- — математика
- — круг/округлять
- — потолок
- — пол
Способы округления чисел
Для округления чисел придумано много способов, они не лишены недостатков, однако часто используются для решения задач. Разберёмся в тонкостях каждого из них.
Если используется стандартная библиотека math, то в начале кода её необходимо подключить. Сделать это можно, например, с помощью инструкции: .
math.ceil() – округление чисел в большую сторону
Функция получила своё имя от термина «ceiling», который используется в математике для описания числа, которое больше или равно заданному.
Любая дробь находится в целочисленном интервале, например, 1.2 лежит между 1 и 2. Функция определяет, какая из границ интервала наибольшая и записывает её в результат округления.
Пример:
math.ceil(5.15) # = 6 math.ceil(6.666) # = 7 math.ceil(5) # = 5
Важно помнить, что функция определяет наибольшее число с учётом знака. То есть результатом округления числа -0.9 будет 0, а не -1.
math.floor() – округление чисел в меньшую сторону
Функция округляет дробное число до ближайшего целого, которое меньше или равно исходному. Работает аналогично функции , но с округлением в противоположную сторону.
Пример:
math.floor(7.9) # = 7 math.floor(9.999) # = 9 math.floor(-6.1) # = -7
math.trunc() – отбрасывание дробной части
Возвращает целое число, не учитывая его дробную часть. То есть никакого округления не происходит, Python просто забывает о дробной части, приводя число к целочисленному виду.
Примеры:
math.trunc(5.51) # = 5 math.trunc(-6.99) # = -6
Избавиться от дробной части можно с помощью обычного преобразования числа к типу int. Такой способ полностью эквивалентен использованию .
Примеры:
int(5.51) # = 5 int(-6.99) # = -6
Нормальное округление
Python позволяет реализовать нормальное арифметическое округление, использовав функцию преобразования к типу int.
И хотя работает по другому алгоритму, результат её использования для положительных чисел полностью аналогичен выводу функции floor(), которая округляет числа «вниз». Для отрицательных аналогичен функции ceil().
Примеры:
math.floor(9.999) # = 9 int(9.999) # = 9 math.ceil(-9.999) # = -9 int(-9.999) # = -9
Чтобы с помощью функции int() округлить число по математическим правилам, необходимо добавить к нему 0.5, если оно положительное, и -0.5, если оно отрицательное.
Тогда операция принимает такой вид: int(num + (0.5 if num > 0 else -0.5)). Чтобы каждый раз не писать условие, удобно сделать отдельную функцию:
def int_r(num): num = int(num + (0.5 if num > 0 else -0.5)) return num
Функция работает также, как стандартная функция округление во второй версии Python (арифметическое округление).
Примеры:
int_r(11.5) # = 12 int_r(11.4) # = 11 int_r(-0.991) # = -1 int_r(1.391) # = 1
round() – округление чисел
round() – стандартная функция округления в языке Python. Она не всегда работает так, как ожидается, а её алгоритм различается в разных версиях Python.
В Python 2
Во второй версии Python используется арифметическое округление. Оно обладает постоянно растущей погрешностью, что приводит к появлению неточностей и ошибок.
Увеличение погрешности вызвано неравным количеством цифр, определяющих, в какую сторону округлять. Всего 4 цифры на конце приводят к округлению «вниз», и 5 цифр к округлению «вверх».
Помимо этого, могут быть неточности, например, если округлить число 2.675 до второго знака, получится число 2.67 вместо 2.68. Это происходит из-за невозможности точно представить десятичные числа типа «float» в двоичном коде.
В Python 3
В третьей версии Python используется банковское округление. Это значит, что округление происходит до самого близкого чётного.
Такой подход не избавляет от ошибок полностью, но уменьшает шанс их возникновения и позволяет программисту добиться большей точности при вычислениях.
Примеры:
round(3.5) # = 4 round(9.5) # = 10 round(6.5) # = 6 round(-6.5) # = -6 round(-7.5) # = -8
Но если вам по каким то причинам нужно округление как в Python 2, то можно воспользоваться функцией написанной нами выше на основе приведения к целому числу.
Округление до сотых
У функции есть ещё один аргумент. Он показывает до какого количества знаков после запятой следует округлять. Таким образом, если нам надо в Python округлить до сотых, этому параметру следует задать значение 2.
Пример округления до нужного знака:
round(3.555, 2) # = 3.56 round(9.515,1) # = 9.5 round(6.657,2) # = 6.66
Math.round Java examples
Math.round is a static method and is part of java.lang.Math class. This method performs the rounding of a floating-point number to the nearest integer or long. There are two overloaded implementations of this method,
- returns the closest integer to the argument.
- returns the closest long to the argument.
Both of these methods always round up.
2.1. Code examples
Below example shows how math round works. It covers both regular scenarios and edge cases.
public class MathRoundingDemo { public static void main(String[] args) { //Round the floating point number to integer //Case 1: Rounding the number to lower whole number System.out.println("Rounding the number to lower whole number"); System.out.println(String.format("3.2 is rounded to - %d", Math.round(3.2))); System.out.println("---------------------------------------------------"); //Case 2: Rounding the number number to next integer System.out.println("Rounding the number number to next integer"); System.out.println(String.format("3.7 is rounded to - %d", Math.round(3.7))); System.out.println("---------------------------------------------------"); //Case 3: When the only number after decimal point is 5, number is rounded to upper whole number System.out.println("When the only number after decimal point is 5, number is rounded to upper whole number"); System.out.println(String.format("3.5 is rounded to - %d", Math.round(3.5))); System.out.println("---------------------------------------------------"); //Case 4: Numbers never double round up System.out.println("Numbers never double round up"); System.out.println(String.format("7.4999 is rounded to - %d", Math.round(7.4999))); System.out.println("---------------------------------------------------"); //Rounding to long value System.out.println("Rounding to long value"); long roundedToLong = Math.round(123234.5); System.out.println("Rounded long value - " + roundedToLong); System.out.println("---------------------------------------------------"); //Rounding the edge case numbers //Case 1: When argument passed is not a number, then ZERO is returned System.out.println("When argument passed is not a number, then ZERO is returned"); System.out.println(String.format("0/0 is rounded to - %d", Math.round(Float.NaN))); System.out.println("---------------------------------------------------"); //Case 2: When negetive infinity is rounded then Long.MIN_VALUE is returned float negativeInfinity = -1/0.0f; int roundedNum = Math.round(negativeInfinity); System.out.println("When negetive infinity is rounded then Long.MIN_VALUE is returned"); System.out.println(String.format("-1/0 is rounded to - %d", roundedNum)); System.out.println("---------------------------------------------------"); //Case 2: When positive infinity is rounded then Long.MAX_VALUE is returned float positiveInfinity = 1/0.0f; int roundedMaxNum = Math.round(positiveInfinity); System.out.println("When positive infinity is rounded then Long.MAX_VALUE is returned"); System.out.println(String.format("1/0 is rounded to - %d", roundedMaxNum)); System.out.println("---------------------------------------------------"); } }
The output of the program looks as below:
MathRoundingDemo.java output
2.2. How to run the sample program
- Save example code to in a directory of your choice.
- Open the command prompt and navigate to the directory where the file is saved.
- Run the command to compile, this will generate .
- To run the example, run command . Don’t specify any extension.
2.3. Edge cases
- When argument passed is a NaN, then it returns ZERO
- When the passed number argument is negative infinity, it returns Long.MIN_VALUE
- When the argument is positive infinity, it returns Long.MAX_VALUE
Literals
Округление при работе с числами ограниченной точности
Реальные физические величины всегда измеряются с некоторой конечной точностью, которая зависит от приборов и методов измерения и оценивается максимальным относительным или абсолютным отклонением неизвестного истинного значения от измеренного, что в десятичном представлении значения соответствует либо определённому числу значащих цифр, либо определённой позиции в записи числа, все цифры после (правее) которой являются незначащими (лежат в пределах погрешности измерения). Сами измеренные параметры записываются с таким числом знаков, чтобы все цифры были надёжными, возможно, последняя — сомнительной. Погрешность при математических операциях с числами ограниченной точности сохраняется и изменяется по известным математическим законам, поэтому когда в дальнейших вычислениях возникают промежуточные значения и результаты с больши́м числом цифр, из этих цифр только часть являются значимыми. Остальные цифры, присутствуя в значениях, фактически не отражают никакой физической реальности и лишь отнимают время на вычисления. Вследствие этого промежуточные значения и результаты при вычислениях с ограниченной точностью округляют до того количества знаков, которое отражает реальную точность полученных значений. На практике обычно рекомендуется при длинных «цепочных» ручных вычислениях сохранять в промежуточных значениях на одну цифру больше. При использовании компьютера промежуточные округления в научно-технических приложениях чаще всего теряют смысл, и округляется только результат.
Так, например, если задана сила 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 кН.
5 Интересный факт о strictfp
В Java есть специальное ключевое слово (strict floating point), которого нет в других языках программирования. И знаете, зачем оно нужно? Оно ухудшает точность работы с вещественными числами. История его появления примерно такова:
Создатели Java:
Мы очень хотим, чтобы Java была суперпопулярна, и программы на Java выполнялись на как можно большем количестве устройств. Поэтому мы прописали в спецификацию Java-машины, что на всех типах устройств все программы должны выполняться одинаково!
Создатели процессора Intel:
Ребята, мы улучшили наши процессоры? и теперь все вещественные числа внутри процессора будет представлены не 8-ю, а 10-ю байтами. Больше байт — больше знаковых цифр. А это значит что? Правильно: теперь ваши научные вычисления будут еще более точными!
Ученые и все, кто занимается сверхточными расчетами:
Круто! Молодцы. Отличная новость.
Создатели Java:
Не-не-не, ребята. Мы же сказали: все Java-программы должны выполняться одинаково на всех устройствах. Принудительно выключаем возможность использования 10 байтовых вещественных чисел внутри процессоров Intel.
Вот теперь все опять отлично! Не благодарите.
Ученые и все, кто занимается сверхточными расчетами:
Да вы там совсем охренели? А ну быстро вернули все как было!
Создатели Java:
Ребята, это для вашей же пользы! Только представьте: все Java-программы выполняются одинаково на всех устройствах. Ну круто же!
Ученые и все, кто занимается сверхточными расчетами:
Нет. Совсем не круто. Быстро вернули все обратно! Или мы вашу Java вам знаете куда засунем?
Создатели Java:
Гм. Что же вы сразу не сказали. Конечно, вернем.
Вернули возможность пользоваться всеми фичами крутых процессоров.
Кстати. Мы так же специально добавили в язык слово : если его написать перед именем функции, вся работа с вещественными числами внутри этой функции будет одинаково плохой на всех устройствах!
Где делать округление: размышления о точности
Теперь, когда есть возможность управлять округлением расчёта, до какого знака следует округлять? Ответ зависит от того, как планируется использовать полученное число.
Вам известна требуемая точность конечного результата из потребностей пользователей. Для чисел, которые будут складываться и вычитаться для получения конечного результата, необходимо добавить ещё один десятичный разряд, так что сумма 0.0144 + 0.0143 будет округлена до 0.03, в то время как если округление выполняется до 0.01, результатом будет 0.02.
Если необходимы числа, которые будут умножаться для получения конечного результата, необходимо сохранять столько знаков после запятой, сколько возможно. Например, коэффициенты и удельные затраты не должны округляться. После умножения необходимо округлять конечный результат.
Заключение
В этой статье я рассказывал в основном об округлении к меньшему по модулю, но есть . В некоторых случаях подходят именно они, и я оставлю читателю возможность изучить их и попробовать реализовать на Go. Но я надеюсь, что теперь вам стало понятно, как устроено округление в Go и как нужно тестировать реализации округления.
Думаю, команда Go приняла правильное решение, добавив функцию Round() в стандартную библиотеку. Без этого мы бы продолжали пользоваться различными некорректными реализациями.
Надеюсь, теперь вам стало ясно, что при работе с float есть много подводных камней, про которые порой забывают даже эксперты. Легко придумать или скопировать откуда-то однострочную реализацию, но сложно написать действительно корректную. Неудивительно, что корректно работающее округление появилось лишь в шестой мажорной версии Java (через 15 лет, прошедших с релиза Java 1.0 до выхода Java 7), и я рад, что Go прошёл этот путь быстрее.