На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (4) 1 [2] 3 4  все  ( Перейти к последнему сообщению )  
> как детектировать переполнение мантиссы в числе с плавающей точкой?
    Eric-S
    Тема большая поэтому всё рассказать не получиться.

    Числа с плавающей точкой состоят из мантиссы и экспоненты. И ещё бывают децентрализованные и нормализованные.
    Проблема в том что перевод чисел из одной системы счисления в другую идут с ошибками округления. Поэтому тут надо будет ещё аккуратно.
    А ещё числа бывают двоичные и десятичные. Десятичные конечно редкость.

    Формат вывода чисел бывает с фиксированной точкой бывает научный (-d.ddd...E+ddd...)

    Мантиса.
    Точность мантисы. Точность можно вычислить
    http://en.wikipedia.org/wiki/Machine_epsil...n_using_C.2B.2B
    Но гораздо проще воспользоваться константой.
    В Си есть библиотека констант.
    std::numeric_limits
    Но так как константу нельзя записать числами из за погрешности при переводи из одной системы счисления(СС) в другую СС.
    Поэтому машинную точность определяют через функцию power
    EpsSingle=power(2,-24);
    EpsDouble=power(2,-53);

    Экспонента.
    Тут уже сказали что будет inf(бесконечность), а не NaN(не число).

    Пересчёт констант из одной СС в другую.
    Константы легко пересчитать для разной СС.
    2^13=10^x
    log_10(2^13)=x
    Или x=log(2^13)/log(10)

    Что касается ловли ошибок.
    У чисел с плавающей точкой есть разные классы ошибок.
    http://www.openwatcom.org/index.php/Math_e...handling_on_x87
    http://msdn.microsoft.com/en-us/library/wi...9(v=vs.85).aspx
    Тип ошибок который стоит обрабатывать надо просто установить.
    http://msdn.microsoft.com/en-us/library/c9...6h(VS.100).aspx

    По умолчанию насколько помню включено деления на 0 и неправильная команда.
    Некоторые графические библиотеки любят работать с определённым типом чисел нормированные/денормальными.

    По поводу общей концепции обработки ошибок. Такого описания я не видел.
    В численных методах есть два движение.
    1. Безошибочные вычисления.
    2. Устойчивые к ошибкам вычисления.

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

    Стив Макконел даёт эту тему тоже в сколь.
    Хотя общие рекомендации такие.
    1. Проверка входных и выходных параметров функции.
    2. Выделения памяти в общем коде и передачу обработчику уже выделенного диапазона памяти.
    3. Сообщения результата о ошибке ниже следующему обработчику для принятия решения. Лично мне результат ошибки нравиться передавать через return, а не вызывать исключение.
    Сообщение отредактировано: Pavia -
      Цитата simsergey @
      Цитата Eric-S @
      Насколько я знаю, эта структура, может изменятся.
      Я так не думаю :)

      Утверждать и настаивать не буду. Просто, мне казалось, что она компиляторо и платформозависимая.

      Цитата simsergey @
      double в с++ использует двоичное основание.

      Конечно же. Ведь компьютер бинарный. Моё удивление происходило из того, что я , как-то не подумал о столь очевидном факте. Точнее, даже и не задумывался.

      Цитата simsergey @
      Если я Вас не так понял, мб переформулируете, или другой приведете пример, что же должна делать функция.. :)

      Отдельный момент, который всплыл при обсуждении, вы поняли правильно. И ваш вариант со структурой, мне гораздо удобнее, чем функция frexp().

      А конкретная функция, должна возвращать число разрядов, которые поместятся в мантиссу без усечения. Если мантисса точностью 52 бита, то в двоичной системе, это 52 разряда. Но в 16-ричной это 13 разрядов. А функция должна вернуть число разрядов мантиссы, для системы счисления, с основанием b.
        Цитата Eric-S @
        Насколько я знаю, эта структура, может изменятся. Есть даже функции для изменения точности.

        Структура может изменяться. Но на самом деле есть стандарт IEEE-754 который и был принят для того чтобы числа не изменялись и на разных архитектура имели одинаковый формат.

        Что касается функции изменения точности, так она только зануляет лишние биты из числ в формате IEEE-754. Так что ей пользоваться не стоит. Вообщем она устаревшая. И такой было чуть ли не во время создания Си.
          Цитата Pavia @
          Числа с плавающей точкой состоят из мантиссы и экспоненты.

          Ага, я это уже давно знаю. Выше, даже приведена структура представления в памяти.

          Цитата Pavia @
          И ещё бывают децентрализованные и нормализованные.

          Об этом только слышал. Что за зверь, не представляю.

          Цитата Pavia @
          Проблема в том что перевод чисел из одной системы счисления в другую идут с ошибками округления. Поэтому тут надо будет ещё аккуратно.

          Ну, это известная проблема. В прочем она решаемая. Я собственно уже давно сделал класс для конвертации числа с плавающей точкой в строку, причём с указанной системой счисления. С округлением, действительно, пришлось туго. Но уже всё норм.
          А вот сейчас, хотел к нему прикрутить дополнительные плюшки. Чтоб лимит знаков, выставлялся автоматически. Дополнительный параметр, раздражает.

          Цитата Pavia @
          А ещё числа бывают двоичные и десятичные. Десятичные конечно редкость.

          Шутите? Угу... Я так и подумал! Знаем, конечно же, и постоянно пользуемся. двоичная и шестнадцатеричная для компа, десятичная для денег, 12 и 24 для часов, 7 ричная для дней недели... И с основанием n, для прикола.

          Цитата Pavia @
          Формат вывода чисел бывает с фиксированной точкой бывает научный (-d.ddd...E+ddd...)

          Угу. Знаемс тоже.
          Кстати, давно мучит вопрос, а как в экспоненциальной записи, оформить 16-ричное число? Или научная исключительно для 10-ричной?
          Это, если конечно же не использовать специальный значок юникода, а писать латинскую e.


          Цитата Pavia @
          Мантиса.
          Точность мантисы. Точность можно вычислить
          http://en.wikipedia.org/wiki/Machine_epsil...n_using_C.2B.2B
          Но гораздо проще воспользоваться константой.
          В Си есть библиотека констант.
          std::numeric_limits
          Но так как константу нельзя записать числами из за погрешности при переводи из одной системы счисления(СС) в другую СС.
          Поэтому машинную точность определяют через функцию power
          EpsSingle=power(2,-24);
          EpsDouble=power(2,-53);

          Опс... Это интересно... Так, побегу читать.
            Цитата Eric-S @
            Это, если конечно же не использовать специальный значок юникода, а писать латинскую e.

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

            Добавлено
            Цитата Eric-S @
            Шутите? Угу... Я так и подумал! Знаем, конечно же, и постоянно пользуемся. двоичная и шестнадцатеричная для компа, десятичная для денег, 12 и 24 для часов, 7 ричная для дней недели... И с основанием n, для прикола.

            В VAX компьютерах была десятичная арифметика реализована прямо в процессоре.
              О! Кажись нашел интересненькое. с std::numeric_limits я уже знаком, но там чего-то не то. Особенно у старого vs 2010.
              А вот константа DBL_MANT_DIG возвращает размер мантиссы, для типа long double. Если я конечно правильно понял. И это число, у меня 53. Ха-эм. Ладно, вычтем битик, на знак.

              Ладненько, теперь надо подумать, как его пересчитать для нужной системы счисления. Для 2-ичной, я разделил на 1, для 16 ричной, я разделил на 4.

              Но забавно, для 10, я тоже должен разделить на 4 и получить 13. Тогда как точно знаю, что у меня влазит 16 разрядов.
              Сообщение отредактировано: Eric-S -
                Цитата Eric-S @
                Но забавно, для 10, я тоже должен разделить на 4 и получить 13. Тогда как точно знаю, что у меня влазит 16 разрядов.

                Не делить. Я же привел пример считаешь через логарифмы а потом округляешь.
                На само деле все 17 разрядов.
                  Цитата Pavia @
                  Поэтому машинную точность определяют через функцию power
                  EpsSingle=power(2,-24);
                  EpsDouble=power(2,-53);

                  ...

                  Пересчёт констант из одной СС в другую.
                  Константы легко пересчитать для разной СС.
                  2^13=10^x
                  log_10(2^13)=x
                  Или x=log(2^13)/log(10)

                  Верно. Я же побежал проверять и не дочитал. Но, ха-эм... с функциями непонятки, кто из них кто?

                  Да и математику, давно уж забыл. Сейчас со скрипом вспоминал, что вообще такое логарифм. А уж какими функциями возводить число в степень или искать логарифм, никогда и не ведал.

                  Значит power() это возведение a, в степень b?
                  А log() это логарифм... Упс, а у него один параметр?
                  Не, надо лезть в справочник. Не знал не знал и забыл.
                    А разве порядок не выясняется через (int)(double >> 52)? (сейчас не проверю)

                    С мантиссой в моей Double как Integer разобрались...
                      Eric-S
                      Цитата Eric-S @
                      Значит power() это возведение a, в степень b?
                      А log() это логарифм... Упс, а у него один параметр?

                      power возведение в степень. Всё верно.
                      Логарифм да.
                      Цитата Eric-S @
                      Не, надо лезть в справочник. Не знал не знал и забыл.

                      Также уже забыл.
                      Насколько помню log - логарифм по основанию 10, ln - натуральный логарифм, другими словами логарифм по основанию e=2.78..
                      По поводу логарифма по произвольному основанию надо посмотреть справку в модуле math должно быть что-то.
                      Но можно реализовать и самому через log или ln или через логарифм с любым основанием.
                      log(x)/log(b) где b основание.
                      ln(x)/ln(b) где b основание.
                        Pavia, благодарю! у меня получилось.
                          Цитата Pavia @
                          Но можно реализовать и самому через log или ln или через логарифм с любым основанием.
                          log(x)/log(b) где b основание.
                          ln(x)/ln(b) где b основание.

                          Круто. Это уже выходит за рамки моих знаний. Но оно работает. Спасибо.

                          Цитата Pavia @
                          На самом деле все 17 разрядов.

                          Ха-эм. На практике у меня 16. Если ставлю 17, то начинаются жуткие проблемы с округлением. Например в последнем разряде, появляются артефакты.

                          А вот написанная функция говорит, что 15. Я правда ткнул мантиссу 52 бита.

                          ExpandedWrap disabled
                            #include <cmath>
                            #include <iostream>
                             
                            int main()
                            {
                             
                            // основание системы счисления
                            int b = 10;
                             
                            // размер мантиссы
                            auto m = std::pow( 2.0, LDBL_MANT_DIG - 1 );
                             
                            // число разрядов
                            auto d = std::log( m ) / std::log( static_cast< long double >( b ) );
                             
                            std::cout << "digits: " << d << std::endl;
                             
                            return 0;
                            }


                          Добавлено
                          Вот получившаяся функция.
                          ExpandedWrap disabled
                            // возвращает максимальное число разрядов для указанной системы счисления
                            std::size_t get_digits_count( unsigned int b )
                            {
                             
                            // число разрядов
                            return static_cast< std::size_t >( std::log( std::pow( 2.0, LDBL_MANT_DIG - 1 ) ) / std::log( static_cast< long double >( b ) ) );
                            }


                          Это альтернативное решение, не касающееся сабжа. А если вернутся к переполнению, то можно проверять, сравнивая число с максимально допустимым значением std::pow( 2.0, LDBL_MANT_DIG - 1 ).


                          Всем спасибо. Тему закрываю.
                          Сообщение отредактировано: Eric-S -
                            Цитата Pavia @
                            Насколько помню log - логарифм по основанию 10, ln - натуральный логарифм, другими словами логарифм по основанию e=2.78..
                            У вас смесь из математики и программирования. Дела таковы:
                            Математика: ln-натуральный, log-общий
                            Программирование: log-натуральный, ln-нет такого (букв мало?)
                              Цитата Славян @
                              Цитата Pavia @
                              Насколько помню log - логарифм по основанию 10, ln - натуральный логарифм, другими словами логарифм по основанию e=2.78..
                              У вас смесь из математики и программирования. Дела таковы:
                              Математика: ln-натуральный, log-общий
                              Программирование: log-натуральный, ln-нет такого (букв мало?)

                              С какой-то радости, математику смешивают с программированием? Это уже давно, две разные науки.
                              Лично мне, функциями понятнее. Даже если я тех функций не знаю. Но по контексту можно догадатся. А вот матзначки, вынесут мой несчастный мозг...
                                Цитата Eric-S @
                                С какой-то радости, математику смешивают с программированием? Это уже давно, две разные науки.
                                Это сильно завязанные науки. Вот log знают все, это общее, это даже начало слова. Но математикам лень использовать общий логарифм, потому они и ввели ln-для натурального (мега-сокращение,да!?), а lg-для десятичного. А вот программистам две буквы - ну уж слижком коротко (в ассемблере и то - редкость), а логарифм нужен. Потому и решили, что пусть log-натуральный, а log10 - десятичный.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (4) 1 [2] 3 4  все


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,3077 ]   [ 16 queries used ]   [ Generated: 3.10.25, 10:14 GMT ]