Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: разработка для Web > Округляшка дает сбой


Автор: MaxPayne 24.11.2014, 00:36
Столкнулся с удивительной штукой в интернет магазине, который я делал на PERL. 

Идет обсчет цены с учетом скидки - все ок. Но сегодня прошла цена 1890 и случилось нечто. PERL обсчитал 0% скидку и выдал цену 1880 (т.е. скидка в 10 рублей). Это мелочи, но хочется узнать в чем проблема. Тестировал с другими ценами - все ок.

Подскажите...

Формула такая: $summafullskidka = (int((($summa/100)*(100-$skidka))/10))*10;
Соответственно SUMMA = 1890, SKIDKA =0.
НА ВЫХОДЕ $summafullskidka = 1880

ИСКАЛ НА КАКОМ ЭТАПЕ И РАЗБИЛ НА ЧАСТИ:
В КОНЦЕ КАЖДОЙ СТРОКИ РЕЗУЛЬТАТЫ
###
$summa11 = $summa/100; 18.9
$summa22 = 100-$skidka;         100
$summa33 = $summa11*$summa22; 1890
$summa44 = $summa33/10; 189
$summa55 = (int($summa44)); 188
$summa66 = $summa55*10; 1880
###

Подключены только два модуля 
use CGI qw(:all);
use DBI;

Автор: igorold 24.11.2014, 07:23
Посмотри результат:

Код

my $rez = sprintf("%.20f",$summa)." ".sprintf("%.20f",$summa/100)."\n";
print $rez;



ну сделай проверку на условие $skidka = 0 или нет.

Этот ответ добавлен с нового Винграда - http://ru.vingrad.com/Okruglyashka-dayet-sboy-id547253f7ae2015e8388b4567#findElement_E7045_5472b2bfae20150a77ddd067_0

Автор: Bulat 24.11.2014, 08:33
Цитата(MaxPayne @  24.11.2014,  00:36 Найти цитируемый пост)
$summa55 = (int($summa44));

Я так понимаю, что в это строке ты пытаешься округлить до целого числа. Только int

Цитата

    int     Returns the integer portion of EXPR. If EXPR is omitted, uses
            $_. You should not use this function for rounding: one because
            it truncates towards 0, and two because machine representations
            of floating-point numbers can sometimes produce counterintuitive
            results. For example, "int(-6.725/0.025)" produces -268 rather
            than the correct -269; that's because it's really more like
            -268.99999999999994315658 instead. Usually, the "sprintf",
            "printf", or the "POSIX::floor" and "POSIX::ceil" functions will
            serve you better than will int().


из перлдока You should not use this function for rounding. ну и далее все описано подробно.

Если нужно именно округление - как вариант

Код

use POSIX qw /ceil/;

....
$summa55 = POSIX::ceil($summa44);
....

Автор: MaxPayne 24.11.2014, 10:41
Цитата(igorold @ 24.11.2014,  08:23)
Посмотри результат:

Код

my $rez = sprintf("%.20f",$summa)." ".sprintf("%.20f",$summa/100)."\n";
print $rez;



ну сделай проверку на условие $skidka = 0 или нет.

Этот ответ добавлен с нового Винграда - http://ru.vingrad.com/Okruglyashka-dayet-sboy-id547253f7ae2015e8388b4567#findElement_E7045_5472b2bfae20150a77ddd067_0

Скидка ИМЕННО 0.

Автор: tzirechnoy 24.11.2014, 14:27
Учитывая натуру перла (динамическая типизацыя, автоматические преобразования, машынные типы) -- я бы посоветовал для рассчёта денег взять Math::BigInt. Очень удобно -- значение всегда int, безо всяких альтэрнатив.

То есть этот случай -- банален, конечно (если нет -- то тем более возьмите Math::BigInt), но он наглядно показывает -- что бывает если floating point использовать с деньгами. Ничего хорошэго. Дажэ если для промежуточных результатов рассчётов.

Автор: MaxPayne 24.11.2014, 17:04
Поставил POSIX::ceil.

Странно, я бы понял такое округление когда число было бы десятичным - типа 189.433234.

А тут то целое число от 189 = 188.

Не ожидал я такого от INT.

Автор: tzirechnoy 24.11.2014, 17:22
1890/10 -- не будет цэлым числом. Поскольку цэлочисленного деления в perl вообще не предусмотрено.

Добавлено через 5 минут и 28 секунд
Да, про POSIX::ceil -- это, честно говоря, редкостный FAIL.

Вы вообще не поняли, что произошло и что Вы сделали. Совсем. Я мог бы подсказать код, который делает правильно в данном конкретном случае -- но, истинно говорю вам, возьмите Math::BigInt и не используйте floating point для денег.
Вы вряд ли скоро поймёте, что такое floating point, что у него за проблемы округления, почему int() в perl сделали именно так, какое округление -- правильное, и, наконец, почему floating point не стоит использовать для денег. Просто поверьте на слово, и возьмите BigInt.

Автор: Bulat 24.11.2014, 18:18
Цитата(MaxPayne @  24.11.2014,  17:04 Найти цитируемый пост)
Не ожидал я такого от INT. 

Скорее поленился прочитать документацию. А чтива там на две минуты и ответ на все вопросы!  smile

Добавлено через 3 минуты и 24 секунды
Цитата(MaxPayne @  24.11.2014,  17:04 Найти цитируемый пост)
А тут то целое число от 189 = 188.

P.S. В Perl нет целых чисел(нет определения типов подобно С).  smile  smile 

Автор: tzirechnoy 24.11.2014, 19:31
Цитата
P.S. В Perl нет целых чисел(нет определения типов подобно С). 


Есть. Например, сравни:

Код
0_ilan@azor /tmp%perl -e 'print sprintf("%30d\n", 2**62+1)'   
           4611686018427387904
0_ilan@azor /tmp%perl -e 'print sprintf("%30d\n", int(2**62)+1)'
           4611686018427387905
0_ilan@azor /tmp%


Несмотря на то, что число 4611686018427387904 вроде бы одно и тожэ -- оно вполне представимо и в цэлочисленном и в виде IEEE754 double, мы видим разницу между цэлым 4611686018427387904 и 4611686018427387904 с плавающей точкой.

Но да, Ларри сделал многое, чтобы этих цэлых чисел было не видно и не слышно. По сути, действительно, можно сказать что цэлых чисел в перле нет -- есть только числа с плавающей точкой, всё остальное -- практически от них неотличимо.

Соответственно, использовать перловые числа для хранения денег -- нельзя. Используйте строки, BigInt или выкручивайтесь ещё как-то.

Автор: Bulat 24.11.2014, 20:58
Цитата(tzirechnoy @  24.11.2014,  19:31 Найти цитируемый пост)

Есть. Например, сравни:

Это пустой холивар... который никому не нужен...

Цитата(tzirechnoy @  24.11.2014,  19:31 Найти цитируемый пост)
использовать перловые числа для хранения денег -- нельзя

Глупость какая... Все зависит от мастерства, таланта и квалификации программиста, а не от того какой язык или технология используется...

Автор: tzirechnoy 25.11.2014, 00:28
Цитата
Все зависит от мастерства, таланта и квалификации программиста,


Я таких умных, которые считают, что всё зависит от их мастерства, таланта и квалификацыи -- периодически на Новорижском вижу. Вокруг них ещё менты и мужыки с болгарками часто ошываются. И труповозка чуть поодаль.

Есть, знаете ли, некоторые простые и достаточно общепринятые правила. Ну там, не более 160 на нашых сраных дорогах (на лучшых из них), полторы секунды резерва, делать бэкапы, тренироваться восстанавливать бэкапы, не хранить деньги в флоатах.

Их можно несоблюдать. Штраф жызнь сама выпишэт.


Автор: Bulat 25.11.2014, 06:56
Цитата(tzirechnoy @  25.11.2014,  00:28 Найти цитируемый пост)
Я таких умных, которые считают, что всё зависит от их мастерства, таланта и квалификацыи -- периодически на Новорижском вижу. Вокруг них ещё менты и мужыки с болгарками часто ошываются. И труповозка чуть поодаль.

Пока что я тебя вижу - строящего из себя самого умного... 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)