powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Java [игнор отключен] [закрыт для гостей] / Простой пример, а не работает.
15 сообщений из 40, страница 2 из 2
Простой пример, а не работает.
    #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
15 сообщений из 40, страница 2 из 2
Форумы / Java [игнор отключен] [закрыт для гостей] / Простой пример, а не работает.
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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