none
Точность типов Double, Float RRS feed

  • Вопрос

  • Можно ли доверять точности встроенных типов Double, Float?

    На странице https://msdn.microsoft.com/en-us/library/678hzkk9.aspx указано про 15-16 знаков точности.

    Что это означает?

    Есть ли в C# аналогичные языку PHP вещи, когда число получает неточный вид (7.99999999918), в результате чего подсчёты могут иметь 1 только правильную цифру, а не 15?

    Подходит ли этот тип для инженерных расчётов или банковских вычислений? Или нужны специальные математические библиотеки? (имеются ли ошибки самих операций +-*/, как быть с накоплением ошибок - можем ли получить гарантированные знаки?)

    Показывается ли при отладке в Visual Studio реальное число правильных цифр результата?


    • Изменено royalpiano 4 января 2016 г. 15:23 добавление
    4 января 2016 г. 15:20

Ответы

  • Вычисления с плавающей точкой имеют множество особенностей о которых должен знать каждый программист. Странно что больше этому не обучают, я помню специальный курс по этой теме (не смотря на то что я учился даже не по компьютерной специализации, а по EE).

    В общем, вам следует почитать несколько статей об этом чтоб понимать что к чему. Да, возможно накопление ошибок, потеря значимости, переполнение и т.д. Это не зависит от языка на котором написана программа. 

    Можете попробовать эту статью:

    https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

    Если трудности с языком то найдите курс для ВУЗ'ов (или как они сейчас называются) на русском.

    Например ваше высказывание про 1 значащую цифру конечно же неверно. Если бы это было так то результат операции 1000+100 был бы равен 1000. (так как 1100 имеет два значащих разряда). Думаю теперь вам понятно что значит 15-16 значащих разрядов?

    Применимость конкретного типа для конкретного случая зависит от многих факторов.

    Так для финансовых расчетов обычно используют тип Decimal (28-29 значащих разрядов).

    Для инженерных расчетов часто достаточно и float (что позволяет экономить память и получать более высокую скорость вычислений). Однако есть тонкости, например использование чисел вроде постоянной Больцмана или заряда электрона могут привести к переполнению.

    В очень редких случаях применяют специальные библиотеки. Я например такие никогда не применял за всю свою практику.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    4 января 2016 г. 17:36
    Модератор

  • Т.е., y=x+1.7152230341146 также подсчитается ВСЕГДА правильно?

    Тут 13 цифр после точки. А в первом 2.



    Вам надо прочитать статью. Лучше несколько. Там как раз и описаны возможные проблемы. Плюс практика конечно. Через месяц-два начнете понимать что к чему. В голове сформируется "список" возможных ошибок и проблем.

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

    Вы так же путайте число значащих разрядов с точностью операций. Это разные вещи. Например если ваши часы показывают 07:53:28 (с точностью до секунды), это вовсе не означает что время именно такое. Может на деле уже 08:02:22, часы просто отстали. Вы же не думайте что раз есть секунды, то именно столько их на самом деле? :)

    Это относится к результатам выполнения функций вроде sin(х). Результат будет иметь 15 разрядов, но не обязательно все из них будут верными. 

    В вашем примере первое число имеет 3 значащих разряда, а во втором - 14. Точность ведь "не после запятой", а всего разрядов. Эти разряды могут быть и до запятой и с запятой по середине или очень даже после запятой. Например после запятой может быть сотня нулей, а потом уже 15 значащих разрядов.

    В любом случае в данном примере числа одного порядка, так что скорее всего получится верное значение (в моем понимании - с возможными ошибками представления и округления).

    Если х будет другое, например сильно больше или сильно меньше то результат может потерять точность. Скажем если х=3.14E100 то получится 3.14E100. Т.е. совершенно ничего не добавится. 

    В общем у меня больше для вас нет советов. Читайте, практикуйтесь. Хотя бы месяц. Удачи.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    6 января 2016 г. 17:14
    Модератор

Все ответы

  • Вычисления с плавающей точкой имеют множество особенностей о которых должен знать каждый программист. Странно что больше этому не обучают, я помню специальный курс по этой теме (не смотря на то что я учился даже не по компьютерной специализации, а по EE).

    В общем, вам следует почитать несколько статей об этом чтоб понимать что к чему. Да, возможно накопление ошибок, потеря значимости, переполнение и т.д. Это не зависит от языка на котором написана программа. 

    Можете попробовать эту статью:

    https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

    Если трудности с языком то найдите курс для ВУЗ'ов (или как они сейчас называются) на русском.

    Например ваше высказывание про 1 значащую цифру конечно же неверно. Если бы это было так то результат операции 1000+100 был бы равен 1000. (так как 1100 имеет два значащих разряда). Думаю теперь вам понятно что значит 15-16 значащих разрядов?

    Применимость конкретного типа для конкретного случая зависит от многих факторов.

    Так для финансовых расчетов обычно используют тип Decimal (28-29 значащих разрядов).

    Для инженерных расчетов часто достаточно и float (что позволяет экономить память и получать более высокую скорость вычислений). Однако есть тонкости, например использование чисел вроде постоянной Больцмана или заряда электрона могут привести к переполнению.

    В очень редких случаях применяют специальные библиотеки. Я например такие никогда не применял за всю свою практику.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    4 января 2016 г. 17:36
    Модератор
  • Спасибо.

    Да, именно эту статью советуют многие. Я её уже начал читать.

    Накопление ошибок, потеря значимости, переполнение... Должен ли этим заниматься сам программист или это уже реализовано в IDE (или в системе)? Там так много всего, что кажется невозможным учесть это в реальной программе. В Визуал Студио уже есть что-то, решающее эти проблемы (хотя бы подсказки)?

    В документации PHP есть такой пример: floor((0.1+0.7)*10). И это оказывается равным не 8, а 7.

    Есть ли то же самое в C#? Если будет 1000 таких сложений, мы потеряем точность. Не говоря про вычисления, где результат важен с большой точностью.

    1100 - первые 2 цифры не 0, это правда. Но с таким же успехом можно было сказать, что важны все 4 разряда.

    Вот в Википедии про потерю точности. Якобы цифры становятся не надёжными.

    https://ru.wikipedia.org/w/index.php?title=%D0%9E%D0%BA%D1%80%D1%83%D0%B3%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5&action=edit&section=5

    Тип Double всё-таки будет гарантировать точное число верных знаков (до запятой и/или после), или надо заниматься этим самому?

    Было бы неплохо иметь подсказку в Visual Studio насчёт этого.

    В теории чисел при работе с длинными числами я использовал BigInteger. Там важна каждая цифра.

    5 января 2016 г. 14:26
  • Это задача программиста так как проблемы проявляются в процессе работы алгоритма. Инструменты не могут знать чего программист хочет.

    Значение 7 вполне возможно. Это ошибка программиста который не знает особенностей работы плавающей точки. Да, конечно, тот же результат будет и в других языках (тут возможны варианты например если используется другой метод округления или другие инструкции процессора, но для каких то значений все равно возможно подобное). Код надо скорректировать для получения ожидаемого результата. Вам задача: как это сделать?

    Эти рассуждения про 4 цифры происходят от непонимания как именно устроена плавающая точка. Дело в том что этих нулей в мантиссе нет, они "спрятаны" в порядке. Мантисса с 1 значащим разрядом всегда будет иметь только один разряд (n*10^p где n целое). T.e. можно представить 0.1 (n=1, p=-1), 1, 10, 1000, но не 1.1, не 11, 110 или 1100. Конечно на деле все еще сложнее так как все это записано в двоичном виде. Для десятичного разряда нужно не менее 4 двоичных, а это больше одного десятичного но меньше двух. Поэтому фактически точность будет 1-2 разряда.

    Тип с плавающей точкой обеспечивает хранение определенного числа значащих разрядов. Что произойдет в процессе вычислений - это на программисте. Определенные инструменты (например Решарпер) дают подсказки по поводу наиболее очевидных ляпов с FP (например сравнение на равенство), но почти все остальное инструментам не под силу.

    Теория и практика часто различаются. До распространения компьютеров (и калькуляторов) инженерные вычисления обычно делали с точностью 3-4 знака. Для 8 битных процессоров были библиотеки плавающей точки с 24 и даже 16 битными числами для повышения скорости работы и экономии памяти. 


    This posting is provided "AS IS" with no warranties, and confers no rights.

    5 января 2016 г. 17:39
    Модератор
  • В C# в документации утверждается про 15 знаков для Double.

    В каких случаях это гарантируется?

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

    Если знаешь, сколько получаешь, то можно и дальше двигаться.

    5 января 2016 г. 21:18
  • Практически всегда используется стандарт IEEE 754. Язык тут обычно не при чем, все определяется реализацией данного стандарта в конкретном процессоре.

    С точки зрения представления данных в случае 64 битных значений double под мантиссу отводится 53 бита (52 явных и старший разряд который всегда принят за 1) что дает чуть меньше чем 16 десятичных знаков (log(2^53)). Таким образом 15 десятичный знаков в представлении чисел гарантируется всегда.

    Ну а получите ли вы верный результат или нет - это уже от вас зависит.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    6 января 2016 г. 0:19
    Модератор
  • То есть, гарантируется минимум 15 верных цифр после запятой и только для представления входных данных?

    Допустим, объявили Double x=3.14. Система гарантирует, что цифры в числе будут правильно учтены, и минимум 15 знаков после точки "дойдут до процессора без ошибок"?

    А что произойдёт после вычислений (сложение, умножение, функции) - неизвестно?

    Или в результате типа Double (после любых вычислений) тоже минимум 15 цифр после запятой тоже будут верные ВСЕГДА независимо от аргументов?

    Т.е., y=x+1.7152230341146 также подсчитается ВСЕГДА правильно?

    Тут 13 цифр после точки. А в первом 2.

    Вычисления всё-таки производит процессор, эта часть зависит не от программиста. Это только если можно указать параметр точности в коде, тогда можно повлиять на точность.

    Вот, к примеру, в Маткаде можно смотреть на число и устанавливать точность - оно изменяется согласно точности.

    Или в PHP BCMath - работа с числами произвольной точности. Вот C# позволяет это? Мне нужно вычислить, допустим, y=sin(X.16-значное число)+cos(X.16-значное число); // с произвольной точностью.


    • Изменено royalpiano 6 января 2016 г. 13:20
    6 января 2016 г. 13:20
  • "Что произойдет в процессе вычислений - это на программисте"

    Есть ли тогда конкретный список возможных ошибок и всего, что может произойти (с типом Double)?

    Чтобы смотреть по списку конкретно и проверять.

    6 января 2016 г. 13:23

  • Т.е., y=x+1.7152230341146 также подсчитается ВСЕГДА правильно?

    Тут 13 цифр после точки. А в первом 2.



    Вам надо прочитать статью. Лучше несколько. Там как раз и описаны возможные проблемы. Плюс практика конечно. Через месяц-два начнете понимать что к чему. В голове сформируется "список" возможных ошибок и проблем.

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

    Вы так же путайте число значащих разрядов с точностью операций. Это разные вещи. Например если ваши часы показывают 07:53:28 (с точностью до секунды), это вовсе не означает что время именно такое. Может на деле уже 08:02:22, часы просто отстали. Вы же не думайте что раз есть секунды, то именно столько их на самом деле? :)

    Это относится к результатам выполнения функций вроде sin(х). Результат будет иметь 15 разрядов, но не обязательно все из них будут верными. 

    В вашем примере первое число имеет 3 значащих разряда, а во втором - 14. Точность ведь "не после запятой", а всего разрядов. Эти разряды могут быть и до запятой и с запятой по середине или очень даже после запятой. Например после запятой может быть сотня нулей, а потом уже 15 значащих разрядов.

    В любом случае в данном примере числа одного порядка, так что скорее всего получится верное значение (в моем понимании - с возможными ошибками представления и округления).

    Если х будет другое, например сильно больше или сильно меньше то результат может потерять точность. Скажем если х=3.14E100 то получится 3.14E100. Т.е. совершенно ничего не добавится. 

    В общем у меня больше для вас нет советов. Читайте, практикуйтесь. Хотя бы месяц. Удачи.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    6 января 2016 г. 17:14
    Модератор