Я повинен обчислити деякі змінні з плаваючою комою, і мій колега пропонує мені використовувати BigDecimal
замість цього, double
оскільки це буде більш точним. Але я хочу знати, що це таке, і як із цього зробити найбільше BigDecimal
?
Я повинен обчислити деякі змінні з плаваючою комою, і мій колега пропонує мені використовувати BigDecimal
замість цього, double
оскільки це буде більш точним. Але я хочу знати, що це таке, і як із цього зробити найбільше BigDecimal
?
Відповіді:
A BigDecimal
- це точний спосіб представлення чисел. A Double
має певну точність. Робота з подвійними різними величинами (скажімо d1=1000.0
і d2=0.001
) може призвести до того, 0.001
що при підсумовуванні цілком знизиться, оскільки різниця у величині настільки велика. З BigDecimal
цим не сталося б.
Недоліком цього BigDecimal
є те, що це повільніше, і алгоритми програмувати таким чином трохи складніше (через +
-
*
і /
не перевантажуються).
Якщо ви маєте справу з грошима, або точність є обов'язковою, використовуйте BigDecimal
. Інакше, Doubles
як правило, досить добре.
Я рекомендую читати Javadoc про , BigDecimal
як вони пояснюють речі краще , ніж я тут :)
if (Math.abs(loadPerServer - maxLoadPerServer) < 0.000001d) {
BigDecimal
", Double буде мати більше "точності" (більше цифр).
Моя англійська це не добре, тому я просто напишу простий приклад тут.
double a = 0.02;
double b = 0.03;
double c = b - a;
System.out.println(c);
BigDecimal _a = new BigDecimal("0.02");
BigDecimal _b = new BigDecimal("0.03");
BigDecimal _c = _b.subtract(_a);
System.out.println(_c);
Вихід програми:
0.009999999999999998
0.01
Хтось все ще хоче використовувати подвійний? ;)
System.out.println(0.003f - 0.002f);
BigDecimal є точним:System.out.println(new BigDecimal("0.003").subtract(new BigDecimal("0.002")));
Є дві основні відмінності від подвійної:
Причина, яку ви повинні використовувати BigDecimal для грошових розрахунків, полягає не в тому, що вона може представляти будь-яке число, а в тому, що вона може представляти всі числа, які можуть бути представлені в десятковому значенні і що включають практично всі числа в грошовому світі (ви ніколи не перераховуєте 1/3 $ комусь).
Якщо ви записуєте дробове значення на зразок 1 / 7
десяткового значення, яке ви отримуєте
1/7 = 0.142857142857142857142857142857142857142857...
з нескінченною послідовністю 142857
. Оскільки ви можете написати лише обмежену кількість цифр, ви неминуче введете помилку округлення (або усічення).
Числа, подібні 1/10
або 1/100
виражені у вигляді двійкових чисел із дробовою частиною, також мають нескінченну кількість цифр після десяткових знаків:
1/10 = binary 0.0001100110011001100110011001100110...
Doubles
зберігати значення як двійкові, і тому може вводити помилку виключно шляхом перетворення десяткового числа у двійкове число, навіть не виконуючи арифметики.
Десяткові числа (як BigDecimal
), з іншого боку, зберігають кожну десяткову цифру як є. Це означає, що десятковий тип не є більш точним, ніж двійкова плаваюча точка або тип фіксованої точки у загальному значенні (тобто він не може зберігатись 1/7
без втрати точності), але він більш точний для чисел, які мають кінцеве число десяткових цифр як часто трапляється для грошових розрахунків.
BigDecimal
Додаткова перевага Java має те, що вона може мати довільну (але кінцеву) кількість цифр з обох сторін десяткової крапки, обмежену лише наявною пам'яттю.
BigDecimal - це чисельна бібліотека Oracle з довільною точністю. BigDecimal є частиною мови Java і корисний для різноманітних застосувань, починаючи від фінансових і закінчуючи науковими.
Немає нічого поганого в тому, щоб використовувати парні для певних розрахунків. Припустимо, однак, ви хотіли обчислити Math.Pi * Math.Pi / 6, тобто значення функції Зети Рімана для реального аргументу двох (проект, над яким я зараз працюю). Ділення з плаваючою комою постає перед вами болісною проблемою помилки округлення.
BigDecimal, з іншого боку, включає безліч варіантів обчислення виразів до довільної точності. Методи додавання, множення та ділення, як описано в документації Oracle нижче "займають місце" +, * та / у BigDecimal Java World:
http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
Метод CompareTo особливо корисний для циклів під час та для нього.
Однак будьте обережні у використанні конструкторів для BigDecimal. Конструктор струн дуже корисний у багатьох випадках. Наприклад, код
BigDecimal onethird = новий BigDecimal ("0,33333333333");
використовує рядкове подання 1/3, щоб представити це нескінченно повторюване число із заданою ступенем точності. Похибка округлення, швидше за все, знаходиться десь так глибоко всередині JVM, що помилки заокруглення не будуть порушувати більшість практичних розрахунків. Я, з особистого досвіду, бачив кругле повзання. Метод setScale важливий у цьому відношенні, як видно з документації Oracle.
/* * Portions Copyright IBM Corporation, 2001. All Rights Reserved. */
Якщо ви маєте справу з розрахунком, є закони про те, як слід обчислити і яку точність ви повинні використовувати. Якщо вам не вдасться, ви зробите щось незаконне. Єдиною реальною причиною є те, що бітове представлення десяткових випадків не є точним. Як просто сказав Василь, найкращим поясненням є приклад. Просто доповнивши його приклад, ось що відбувається:
static void theDoubleProblem1() {
double d1 = 0.3;
double d2 = 0.2;
System.out.println("Double:\t 0,3 - 0,2 = " + (d1 - d2));
float f1 = 0.3f;
float f2 = 0.2f;
System.out.println("Float:\t 0,3 - 0,2 = " + (f1 - f2));
BigDecimal bd1 = new BigDecimal("0.3");
BigDecimal bd2 = new BigDecimal("0.2");
System.out.println("BigDec:\t 0,3 - 0,2 = " + (bd1.subtract(bd2)));
}
Вихід:
Double: 0,3 - 0,2 = 0.09999999999999998
Float: 0,3 - 0,2 = 0.10000001
BigDec: 0,3 - 0,2 = 0.1
Також ми маємо це:
static void theDoubleProblem2() {
double d1 = 10;
double d2 = 3;
System.out.println("Double:\t 10 / 3 = " + (d1 / d2));
float f1 = 10f;
float f2 = 3f;
System.out.println("Float:\t 10 / 3 = " + (f1 / f2));
// Exception!
BigDecimal bd3 = new BigDecimal("10");
BigDecimal bd4 = new BigDecimal("3");
System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4)));
}
Дає нам вихід:
Double: 10 / 3 = 3.3333333333333335
Float: 10 / 3 = 3.3333333
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion
Але:
static void theDoubleProblem2() {
BigDecimal bd3 = new BigDecimal("10");
BigDecimal bd4 = new BigDecimal("3");
System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4, 4, BigDecimal.ROUND_HALF_UP)));
}
Має вихід:
BigDec: 10 / 3 = 3.3333