powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Java [игнор отключен] [закрыт для гостей] / Простой пример, а не работает.
40 сообщений из 40, показаны все 2 страниц
Простой пример, а не работает.
    #34869897
Базаров
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добрый день, господа.
Пишу программу. Получая исходные данные в виде суммы кредита, рассчитывает сумму ануитетного платежа. Код:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
 import  java.util.Scanner;

 public   class  credit {
	
	 public   static   void  main(String[] args) {
		
		//получаем данные
		
		System.out.println("Введите сумму?");
		Scanner sc =  new  Scanner(System.in);
		 int  summ = sc.nextInt();
		
		System.out.println("Введите процентъ?");
		 int  interest = sc.nextInt();
		
		System.out.println("Введите срокъ?");
		 int  term = sc.nextInt();
		
		//производим вычисления
		
		 int  totalMonth = term* 12 ;
		
		 double   totalSumm = summ;
		 double  totalMinusSumm= 0 . 0 ;
		 double  minusSumm= 0 . 0 ;
		
		
		 for  ( int  i=totalMonth; i>= 0 ; i--) {
			totalSumm = (totalSumm* 0 . 01 )+totalSumm;
			minusSumm = totalSumm/i;
			totalMinusSumm=totalMinusSumm+minusSumm;
			totalSumm=totalSumm-minusSumm;
			
		}
		
		System.out.println("Средний ежемесячный платёж будет составлять " + totalMinusSumm/totalMonth + " американских рублей!");
		System.out.println("всего придеццо отдать " + totalMinusSumm + " !");
		System.out.println("Переплата составит " + (totalMinusSumm-summ) + " !");
		

	}

}

======= в результате получаем:

Средний ежемесячный платёж будет составлять NaN американских рублей!
всего придеццо отдать NaN !
Переплата составит NaN !

Почему NaN???!! Где упустил? Вторые сутки голову ломаю...
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34869954
Фотография fixxer
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В последней итерации цикла будет деление на нуль.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34869960
max_from_sumy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
На ноль делить нельзя)))

Код: plaintext
1.
2.
3.
4.
5.
6.
 for  ( int  i=totalMonth; i>= 0 ; i--) {
totalSumm = (totalSumm* 0 . 01 )+totalSumm;
minusSumm = totalSumm/i; //<< здесь деление на ноль при i=0
totalMinusSumm=totalMinusSumm+minusSumm;
totalSumm=totalSumm-minusSumm;
	
}
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34870028
Базаров
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Спасибо за помощь!!!!!!
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34870250
Vurn
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Все финансовые расчеты в Java надо делать строжайше в BigDecimal! BigDecimal для них и предназначен.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34870321
Leonidv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да ладно, этож курсовая или лаба :)
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34870340
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
VurnВсе финансовые расчеты в Java надо делать строжайше в BigDecimal! BigDecimal для них и предназначен.
Излишне категорично, поэтому ерунда.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34870366
Vurn
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Софтверный проктолог VurnВсе финансовые расчеты в Java надо делать строжайше в BigDecimal! BigDecimal для них и предназначен.
Излишне категорично, поэтому ерунда.

Вывод сделан на основе проигранного судебного дела, вызванного ошибочными расчетами сумм из-за неточности float & double/
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34870408
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Vurn
Вывод сделан на основе проигранного судебного дела, вызванного ошибочными расчетами сумм из-за неточности float & double/
Мне было бы очень интересно взглянуть на финансовый расчет, в котором не хватает 17 цифр после запятой. Хотя бы порядок чисел и проводимые операции, вас не затруднит?
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34870410
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Простите, 15 цифр. Но не суть.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34870412
Фотография agrasoff
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
VurnВывод сделан на основе проигранного судебного дела, вызванного ошибочными расчетами сумм из-за неточности float & double/о! а чо - на зоне инет уже провели?
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34870483
Самоловских Виталий aka Kefir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Софтверный проктологПростите, 15 цифр. Но не суть.
Бюджет сша в йенах. :)
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34870493
Самоловских Виталий aka Kefir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Проблема не в порядке величин, а в том что десятые доли не точно представляются в двоичной системе счисления. И в зависимости от уже порядка точность представления разные и округление после различных операций может осуществляться в разные стороны.

Еще одна проблема связана со сравнением величин с плавающей точкой. В любом учебнике по программированию Вы найдете фразу о том что крайне нежелательно использовать == для сравнения чисел с плавающей запятой. Для сравнения таких чисел сравнивают абсолютную величину их разности с константой или берут отношение абсолютной разности и наибольшего по модулю числа и сравнивают с константой.

Не выполняются некоторые аксиомы, например:
a*c + b*c == (a+b)*c, выполняется не всегда, почти никогда.

BigDecimal - быстрый и удобный, смысла в использовании double НЕТ.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34870701
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Самоловских Виталий aka KefirПроблема не в порядке величин, а в том что десятые доли не точно представляются в двоичной системе счисления. И в зависимости от уже порядка точность представления разные и округление после различных операций может осуществляться в разные стороны.

Еще одна проблема связана со сравнением величин с плавающей точкой. В любом учебнике по программированию Вы найдете фразу о том что крайне нежелательно использовать == для сравнения чисел с плавающей запятой. Для сравнения таких чисел сравнивают абсолютную величину их разности с константой или берут отношение абсолютной разности и наибольшего по модулю числа и сравнивают с константой.

Не выполняются некоторые аксиомы, например:
a*c + b*c == (a+b)*c, выполняется не всегда, почти никогда.

BigDecimal - быстрый и удобный, смысла в использовании double НЕТ.
А зачем это делать?
Зачем точно сравнивать числа?
Если уж когда и приходилось высчитывать циферки — алгоритм был прост: зачитать данные из СУБД, провести над ними ряд операций (сложений, умножений, делений, вычитаний) и запихнуть обратно в СУБД. При том, что не видел в своей жизни БД, где бы для хранения вещественных чисел использовались поля в таблицах размерности больше чем что-то в районе number(24,10).

У меня такое ощущения, что я чего-то не понимаю :) Просто, наверное, не те циферки считаю :)
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34871417
Самоловских Виталий aka Kefir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Софтверный проктолог
А зачем это делать?
Зачем точно сравнивать числа?

C нулем сравнивать иногда приходится.


При том, что не видел в своей жизни БД, где бы для хранения вещественных чисел использовались поля в таблицах размерности больше чем что-то в районе number(24,10).

Тип money в MS SQL Server 8 байт. Не больше чем тип float в той же СУБД (double в Java).


У меня такое ощущения, что я чего-то не понимаю :) Просто, наверное, не те циферки считаю :)
Я рад, что Вам никогда не приходилось сталкиваться с проблемами с потерей точности. Но я то всю время учебы в Универе разрабатывал расчетные программы и все эти проблемы "пощупал руками".

И еще, тип BigDecimal дает абсолютную свободу для округления, хочешь вверх, хочешь вниз, хочешь наискосок :) .

Еще можно поспорить о скорости работы с этими типами. С одной стороны целочисленные вычисления выполняются быстрее и целочисленных операций за единицу времени может быть выполнено больше. Кроме того, в Power 6 включили операции для работы с десятичной дробью. С другой стороны, в современных процессорах вычисления с плавающей запятой так же выполняются достаточно быстро, а тип BigDecimal вынуждает тратить процессорное время и память на накладные расходы.

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

К чему я? Так вот, чем рисковать потерять точность, а потом искать куда делась копейка, лучше сразу использовать BigDecimal.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34871481
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Самоловских Виталий aka KefirТип money в MS SQL Server 8 байт. Не больше чем тип float в той же СУБД (double в Java).

Я не пользуюсь MS SQL.
Я никогда не пользую в СУБД такие типы данных как money или float — только number с ручным указанием точности.
На моей практике встречались number(24,10), numeric(18,8) и numeric(20,5) — в каждом отдельном случае это всех устраивало. В некоторых расчетах всегда округляют промежуточные суммы до четырех знаков.

Тип double в Java хранит 15 знаков после запятой. И как уже говорил — не представляю потери копейки в каких бы то ни было расчетах (если у программиста руки не из жопы растут).
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34871568
wessen
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Софтверный проктолог
Тип double в Java хранит 15 знаков после запятой. И как уже говорил — не представляю потери копейки в каких бы то ни было расчетах (если у программиста руки не из жопы растут).

Не один вменяемый программист никогда в жизни не напишет программу, которая рассчитывает деньги с использованием double. Тип double дает большую погрешность, вот что на сайте sun написано про float и double:
http://java.sun.com/docs/books/tutorial/java/nutsandbolts/datatypes.html
This data type should never be used for precise values, such as currency. For that, you will need to use the java.math.BigDecimal class instead.


Удивительно, что для некоторых это новость.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34871582
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wessen
Не один вменяемый программист никогда в жизни не напишет программу, которая рассчитывает деньги с использованием double. Тип double дает большую погрешность, вот что на сайте sun написано про float и double:
http://java.sun.com/docs/books/tutorial/java/nutsandbolts/datatypes.html
This data type should never be used for precise values, such as currency. For that, you will need to use the java.math.BigDecimal class instead.


Удивительно, что для некоторых это новость.
Будем считать, что я не рассчитываю деньги.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34872202
Vurn
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вот для примера - обычная задача тех, кто работает с финансами.
Суммы налога, составляющие ЕСН (а это 6 налогов на каждого сотрудника ежемесячно) должны рассчитываться с точностью строго 5 знаков после запятой (не больше и не меньше) и накапливаться в течении года. Причем, 5 знаков после запятой и для зарплаты в 1000 рублей и для 100 миллонов.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34872943
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
VurnВот для примера - обычная задача тех, кто работает с финансами.
Суммы налога, составляющие ЕСН (а это 6 налогов на каждого сотрудника ежемесячно) должны рассчитываться с точностью строго 5 знаков после запятой (не больше и не меньше) и накапливаться в течении года. Причем, 5 знаков после запятой и для зарплаты в 1000 рублей и для 100 миллонов.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
     public   static   void  A()  throws  Exception {
         double  value = 1548d;

         double  x1 =  0 .01d;
         double  x2 =  0 .4d;
         double  x3 =  0 .05d;
         double  x4 =  0 .07d;
         double  x5 =  0 .09d;
         double  x6 =  0 .15d;

         double  sum = 0d;
         for  ( int  count =  0 ; count <  100000 ; count++) {
            sum = 0d;
             for  ( int  i =  1 ; i <=  12 ; i++) {
                sum += value * x1 + value * x2 + value * x3 + value * x4 + value * x5 + value * x6;
            }
        }
        System.out.println("DOUBLE. VALUE = " + value + ", SUMM = " + sum + " :: round " +
                            new  BigDecimal(sum).setScale( 5 , BigDecimal.ROUND_HALF_EVEN));

    }

     public   static   void  B()  throws  Exception {
        BigDecimal value =  new  BigDecimal(1548d);

        BigDecimal x1 =  new  BigDecimal( 0 .01d);
        BigDecimal x2 =  new  BigDecimal( 0 .4d);
        BigDecimal x3 =  new  BigDecimal( 0 .05d);
        BigDecimal x4 =  new  BigDecimal( 0 .07d);
        BigDecimal x5 =  new  BigDecimal( 0 .09d);
        BigDecimal x6 =  new  BigDecimal( 0 .15d);

        BigDecimal sum =  new  BigDecimal(0d);
         for  ( int  count =  0 ; count <  100000 ; count++) {
            sum =  new  BigDecimal(0d);
             for  ( int  i =  1 ; i <=  12 ; i++) {
                sum = sum.add(value.multiply(x1)).add(value.multiply(x2)).add(value.multiply(x3)).add(value.
                        multiply(x4)).add(value.multiply(x5)).add(value.multiply(x6));
            }
        }
        System.out.println("BIGDECIMAL. VALUE = " + value + ", SUMM = " + sum + " :: round " +
                           sum.setScale( 5 , BigDecimal.ROUND_FLOOR));

    }

Код: plaintext
1.
2.
3.
4.
 DOUBLE . VALUE =  1548 . 0 , SUMM =  14303 . 519999999997  :: round  14303 . 52000 
Time =  16  msec
BIGDECIMAL. VALUE =  1548 , SUMM =  14303 . 52000000000042664871635622603207593783736228942871093750000  :: round  14303 . 52000 
Time =  7843  msec
Вы по прежнему утверждаете, что для приведенного вами примера double не подходит? Обратите внимание и на время, заодно.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34872951
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Для зарплаты в сто миллионов (102 458 787):

Код: plaintext
1.
2.
3.
4.
5.
 DOUBLE . VALUE =  1 .02458787E8, SUMM =  9 .467191918800001E8 :: round  946719191 . 88000 
Time =  31  msec
BIGDECIMAL. VALUE =  102458787 , SUMM =  946719191 . 88000002823895991793667903380082861986011266708374023437500  :: round  946719191 . 88000 
Time =  7734  msec

...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34872955
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Рассчитывая доход корпорации Microsoft в карбованцах несомненно следует пользоваться BigDecimal. А для простых парней, не оперирующих суммами более миллиарда — зачем?
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34873464
Самоловских Виталий aka Kefir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
1. Ну это Вы сейчас суммами больше миллиарда не оперируете. А потом кто его знает как пойдет. Вот я уже приближаюсь. Т.е. я уже сейчас использую 9+4 = 13 знаков.

2. Когда Вы создавали BigDecimal при помощи конструктора BigDecimal(double) Вы внесли ошибку в данные, т.к. тип double вносит ошибку в десятичную дробь. Естественно, при этом результаты вычислений оказались идентичными. А размер BigDecimal оказался гораздо больше, чем требовалось для хранения реальных данных, отсюда такая разница в производительности.

Если предположить, что BigDecimal мог быть в 5 раз короче, то операция произведения требовала бы в 25 раз меньше времени.

3. Время выполнения теста корректно снимать только после прогона теста несколько раз. Из-за особенностей работы Hotspot. Т.к. BigDecimal - класс Java, логично подождать пока он откомпилируется с более высоким уровнем оптимизации.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34873472
Самоловских Виталий aka Kefir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
    
 public   static   void  useDouble()  throws  Exception {
         double  value = 1548d;

         double  x1 =  0 .01d;
         double  x2 =  0 .4d;
         double  x3 =  0 .05d;
         double  x4 =  0 .07d;
         double  x5 =  0 .09d;
         double  x6 =  0 .15d;

         double  sum = 0d;
         for  ( int  count =  0 ; count <  100000 ; count++) {
            sum = 0d;
             for  ( int  i =  1 ; i <=  12 ; i++) {
                sum += value * x1 + value * x2 + value * x3 + value * x4 + value * x5 + value * x6;
            }
        }
        System.out.println("DOUBLE. VALUE = " + value + ", SUMM = " + sum + " :: round " +
                 new  BigDecimal(sum).setScale( 5 , BigDecimal.ROUND_HALF_EVEN));

    }

     public   static   void  useBigDecimal()  throws  Exception {
        BigDecimal value =  new  BigDecimal(1548d);

        BigDecimal x1 =  new  BigDecimal( 0 .01d).setScale( 2 , RoundingMode.HALF_UP);
        BigDecimal x2 =  new  BigDecimal( 0 .4d).setScale( 2 , RoundingMode.HALF_UP);
        BigDecimal x3 =  new  BigDecimal( 0 .05d).setScale( 2 , RoundingMode.HALF_UP);
        BigDecimal x4 =  new  BigDecimal( 0 .07d).setScale( 2 , RoundingMode.HALF_UP);
        BigDecimal x5 =  new  BigDecimal( 0 .09d).setScale( 2 , RoundingMode.HALF_UP);
        BigDecimal x6 =  new  BigDecimal( 0 .15d).setScale( 2 , RoundingMode.HALF_UP);

        BigDecimal sum = BigDecimal.ZERO;
         for  ( int  count =  0 ; count <  100000 ; count++) {
            sum = BigDecimal.ZERO;
             for  ( int  i =  1 ; i <=  12 ; i++) {
                sum = sum.add(value.multiply(x1)).add(value.multiply(x2)).add(value.multiply(x3)).add(value.
                        multiply(x4)).add(value.multiply(x5)).add(value.multiply(x6));
            }
        }
        System.out.println("BIGDECIMAL. VALUE = " + value + ", SUMM = " + sum + " :: round " + sum);
    }
Код: plaintext
1.
2.
3.
4.
DOUBLE. VALUE = 1548.0, SUMM = 14303.519999999997 :: round 14303.52000
Time in nanoseconds: 9615194
BIGDECIMAL. VALUE = 1548, SUMM = 14303.52 :: round 14303.52
Time in nanoseconds: 4034282131

Разница примерно в 400 раз. Варьируется.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34873484
Самоловских Виталий aka Kefir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пример: Рассчет суммы с НДС для 1000 наименований с суммами по наименованиям от 1 копейки до 100000рублей. Вполне реальная задача.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
    
 private   static   final   double  DOUBLE_NDS =  0 .18d;
     private   static   final  BigDecimal BIG_DECIMAL_NDS =  new  BigDecimal(DOUBLE_NDS).setScale( 2 , RoundingMode.HALF_UP);

     public   static   void  useDouble()  throws  Exception {
         double  sum =  0 ;
         for  ( int  i =  1 ; i <=  500 ; i++) {
             double  sum1 = (( double ) i) /  100 ;
             double  sum2 = (( double ) ( 10000000  + i *  10000  + i)) /  100 ;

            sum += sum1 * DOUBLE_NDS;
            sum += sum2 * DOUBLE_NDS;
        }

        System.out.println("double:");
        System.out.println("Сумма: " + sum);
        System.out.println("После округления: " +  new  BigDecimal(sum).setScale( 2 , RoundingMode.HALF_UP));
    }

     public   static   void  useBigDecimal()  throws  Exception {
        BigDecimal sum = BigDecimal.ZERO;
         for  ( int  i =  1 ; i <=  500 ; i++) {
            BigDecimal sum1 =  new  BigDecimal(i).movePointLeft( 2 );
            BigDecimal sum2 =  new  BigDecimal(( double ) ( 10000000  + i *  10000  + i)).movePointLeft( 2 );

            sum = sum.add(sum1.multiply(BIG_DECIMAL_NDS).setScale( 2 , RoundingMode.HALF_UP));
            sum = sum.add(sum2.multiply(BIG_DECIMAL_NDS).setScale( 2 , RoundingMode.HALF_UP));
        }

        System.out.println("BigDecimal:");
        System.out.println("Сумма: " + sum);
        System.out.println("После округления: " + sum.setScale( 2 , RoundingMode.HALF_UP));
    }
А вот и результат:

double:
Сумма: 1.12549509E7
После округления: 11254950.90

BigDecimal:
Сумма: 11254951.00
После округления: 11254951.00

Расхождение на 10 копеек.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34873488
Самоловских Виталий aka Kefir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добавил округление для случая с double на каждом шаге:

Код: plaintext
1.
2.
3.
     private   static   double  round( double  value){
         return   new  BigDecimal(value).setScale( 2 , RoundingMode.HALF_UP).doubleValue();
    }

Результат:

double:
Сумма: 1.1254950830000011E7
После округления: 11254950.83

BigDecimal:
Сумма: 11254951.00
После округления: 11254951.00

Расхождение на 17 копеек.

Вы все еще уверены, что тип double подходит для финансовых расчетов?
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34874586
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Самоловских Виталий aka Kefir
Вы все еще уверены, что тип double подходит для финансовых расчетов?
И вот опять...
Как только я вижу попытки доказать, что double — говно, так тут же доказывальщик допускает ряд ошибок :)

Я для вас подготовил занятный пример. Обратите внимание в район Step 25.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
     public   static   void  useBigDecimal()  throws  Exception {
        BigDecimal sum = BigDecimal.ZERO;
         double  dsum =  0 ;

         for  ( int  i =  1 ; i <=  500 ; i++) {
            BigDecimal sum1 =  new  BigDecimal(i).movePointLeft( 2 );
            BigDecimal sum2 =  new  BigDecimal(( double ) ( 10000000  + i *  10000  + i)).movePointLeft( 2 );

             double  dsum1 = (( double ) i) / 100d;
             double  dsum2 = (( double ) (10000000d + i * 10000d + i)) / 100d;

             if  (sum1.doubleValue() != dsum1 || sum2.doubleValue() != dsum2) {
                System.out.println("!!NON EQUALS");

                System.out.println("DB sum1 = " + sum1 + ", d sum1 = " + dsum1);
                System.out.println("DB sum2 = " + sum2 + ", d sum2 = " + dsum2);

            }
            System.out.println("\n Step " + i);

            System.out.println("DB = " + sum.add(sum1.multiply(BIG_DECIMAL_NDS)) + " and round = " +
                               sum.add(sum1.multiply(BIG_DECIMAL_NDS).setScale( 2 , RoundingMode.HALF_UP)));
            System.out.println("d = " + (dsum + dsum1 * DOUBLE_NDS) + " and round = " +
                               round(dsum + dsum1 * DOUBLE_NDS));

            sum = sum.add(sum1.multiply(BIG_DECIMAL_NDS).setScale( 2 , RoundingMode.HALF_UP));
            dsum = round(dsum + dsum1 * DOUBLE_NDS);

            System.out.println("STEP1. DB sum = " + sum + ", d sum = " + dsum);

            System.out.println("DB = " + sum.add(sum2.multiply(BIG_DECIMAL_NDS)) + " and round = " +
                               sum.add(sum2.multiply(BIG_DECIMAL_NDS).setScale( 2 , RoundingMode.HALF_UP)));
            System.out.println("d = " + (dsum + dsum2 * DOUBLE_NDS) + " and round = " +
                               round(dsum + dsum2 * DOUBLE_NDS));

            sum = sum.add(sum2.multiply(BIG_DECIMAL_NDS).setScale( 2 , RoundingMode.HALF_UP));
            dsum = round(dsum + dsum2 * DOUBLE_NDS);

            System.out.println("STEP2. DB sum = " + sum + ", d sum = " + dsum);

        }

        System.out.println("BigDecimal:");
        System.out.println("Сумма: " + sum);
        System.out.println("После округления: " + sum.setScale( 2 , RoundingMode.HALF_UP));

        System.out.println("double:");
        System.out.println("Сумма: " + dsum);
        System.out.println("После округления: " +  new  BigDecimal(dsum).setScale( 2 , RoundingMode.HALF_UP));

    }

Там очень интересно будет:

DB = 455851.1750 and round = 455851.18
d = 455851.175 and round = 455851.17
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34874612
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Кстати, касательно скорости...
Для интересно запускал в режиме -server -Xms32m -Xmx256m.
Разница — 150-300 раз.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34874620
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Как итог — если думать о том, что делаешь, не хочется (или хочется дополнительной уверенности в действиях) — лучше использовать BigDecimal.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34876651
Vurn
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Step 500
DB = 11227950.1000 and round = 11227950.10
d = 1.122795001E7 and round = 1.122795001E7
STEP1. DB sum = 11227950.10, d sum = 1.122795001E7
DB = 11254951.0000 and round = 11254951.00
d = 1.125495091E7 and round = 1.125495091E7
STEP2. DB sum = 11254951.00, d sum = 1.125495091E7
BigDecimal:
Сумма: 11254951.00
После округления: 1125495 1.00
double:
Сумма: 1.125495091E7
После округления: 1125495 0.91


Итоговая сумма расползлась на 9 копеек.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34876806
Самоловских Виталий aka Kefir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Софтверный проктологКак итог — если думать о том, что делаешь, не хочется (или хочется дополнительной уверенности в действиях) — лучше использовать BigDecimal.
Я ф шоке:
Код: plaintext
System.out.println( new  BigDecimal( 0 .175d));
Дает: 0.174999999999999988897769753748434595763683319091796875
Почему так, совершенно непонятно, т.к. 0.175 может быть представлено точно в типе double. Надо будет покопаться в исходниках.

Может еще от версии JRE зависит...

Исправил round:
Код: plaintext
1.
2.
3.
4.
5.
6.
 private   static   double  round( double  value){
         return   new  BigDecimal(value).
                setScale( 4 , RoundingMode.HALF_UP).
                setScale( 2 , RoundingMode.HALF_UP).
                doubleValue();
    }

Действительно все сошлось. Вот только такой вариант double приемлем ТОЛЬКО когда точно знаешь, что точность выше 4 знаков после запятой понадобиться не может. Общий случай, я, честно говоря, написать затрудняюсь.

Такой вариант:
Код: plaintext
1.
2.
3.
     private   static   double  round( double  value){
         return  Math.round(value*100d)/100d;
    }

Дал:
double:
Сумма: 1.1254950960000008E7
После округления: 11254950.96

BigDecimal:
Сумма: 11254951.00
После округления: 11254951.00

Расхождение в 4 копейки.

РЕЗЮМЕ: Чем трахаться с округлениями лучше я буду пользовать BigDecimal. Пусть синтетические тесты и работают в 150 (Vurn) - 400 (Мой) раз медленнее (единственное оправдание для использования double), эти расчеты еще ни разу не становились узким местом в системах, которые я разрабатываю.

Если уж очень узкое место, можно попробовать создать собственный тип на основе long. Он должен работать значительно быстрее.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34876807
Самоловских Виталий aka Kefir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вру: 0.175 не может быть представлена в виде double точно. Так что, все объяснимо.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34876816
Самоловских Виталий aka Kefir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А вот что я обнаружил, когда замерил время на выполнение варианта с двойным округлением при помощи BigDecimal (Единственным, который сошелся).

double:
Сумма: 1.1254951000000007E7
После округления: 11254951.00
Time in nanoseconds: 21724552

BigDecimal:
Сумма: 11254951.00
После округления: 11254951.00
Time in nanoseconds: 2556651

Что мы имеем? В этом случае BigDecimal оказался в 8 раз эффективнее!!!

Вот еще более удивительный результат. При использовании Math.round(value*100)/100 (Этот вариант дает некорректный ответ) я получил:

double:
Сумма: 1.1254950960000008E7
После округления: 11254950.96
Time in nanoseconds: 1301138

BigDecimal:
Сумма: 11254951.00
После округления: 11254951.00
Time in nanoseconds: 2628739

Т.е. вариант с double оказался только в 2!!! раза эффективнее, чем с BigDecimal.

Лично у меня сомнений не осталось.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34877438
expp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Самоловских Виталий aka Kefir
Я ф шоке:
Код: plaintext
System.out.println( new  BigDecimal( 0 .175d));
Дает: 0.174999999999999988897769753748434595763683319091796875
Почему так, совершенно непонятно, т.к. 0.175 может быть представлено точно в типе double. Надо будет покопаться в исходниках.
Осторожно!!! есть ещё и сташное число 0.25 - тоже компактно суммой степеней двоек не представляется. (копаться придётся в процессоре. Где твой туннельный микроскоп?)
Самоловских Виталий aka KefirМожет еще от версии JRE зависит...
нет. это ошибка в IEEE754
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34877700
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Правда интересные вещи всплывают, как только начинаешь разбираться?

Кстати, таки да. Если требуются точные округления в процессе вычисления — double использоваться не нужно. На одну копейку не сходится. Ну, раньше у меня таких задач никогда и не стояло, а теперь бубу знать
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34877711
Фотография Софтверный проктолог
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
P.S. Кстати, настоящие пацаны double'ы округляют так:
Код: plaintext
1.
( double ) Math.round(value * 100d) / 100d
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34878000
Vurn
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Софтверный проктологP.S. Кстати, настоящие пацаны double'ы округляют так:
Код: plaintext
1.
( double ) Math.round(value * 100d) / 100d

А вот с таким округлением все отработает просто идеально. Но это лишь частный случай.
Код: plaintext
1.
StrictMath.floor(value * 100d +  0 .500001d) / 100d;
[/quot]
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34878373
Самоловских Виталий aka Kefir
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Софтверный проктологP.S. Кстати, настоящие пацаны double'ы округляют так:
Код: plaintext
1.
( double ) Math.round(value * 100d) / 100d

Как я уже написал, дает ошибку, к тому же пример с таким способом округления работает всего в 2 раза быстрее чем пример полностью на BigDecimal.

Кстати, из JDK java.lang.Math:
Код: plaintext
1.
2.
3.
     public   static   long  round( double  a) {
	 return  ( long )floor(a +  0 .5d);
    }
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34879618
Vurn
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
expp Самоловских Виталий aka Kefir
Я ф шоке:
Код: plaintext
System.out.println( new  BigDecimal( 0 .175d));
Дает: 0.174999999999999988897769753748434595763683319091796875
Почему так, совершенно непонятно, т.к. 0.175 может быть представлено точно в типе double. Надо будет покопаться в исходниках.
Осторожно!!! есть ещё и сташное число 0.25 - тоже компактно суммой степеней двоек не представляется. (копаться придётся в процессоре. Где твой туннельный микроскоп?)
Самоловских Виталий aka KefirМожет еще от версии JRE зависит...
нет. это ошибка в IEEE754

В документации написано, что BigDecimal из float/double рекомендуется делать через String.
...
Рейтинг: 0 / 0
Простой пример, а не работает.
    #34879632
Vurn
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добавлю в тему. В BigDecimal быстрей всего получается умножение, в два-три раза медленней - сложение/вычитанией, и 20 раз медленней - деление.
...
Рейтинг: 0 / 0
40 сообщений из 40, показаны все 2 страниц
Форумы / Java [игнор отключен] [закрыт для гостей] / Простой пример, а не работает.
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]