Как я могу обнаружить потерю точности из-за округления как при сложении с плавающей запятой, так и при умножении?

Из компьютерных систем: взгляд программиста:

С плавающей запятой одинарной точности

  • выражение (3.14f+1e10f)-1e10f оценивается как 0,0: значение 3,14 теряется из-за округления.

  • выражение (1e20f*1e20f)*1e-20f оценивается как +∞ , а 1e20f*(1e20f*1e-20f) оценивается как 1e20f.

  • Как я могу обнаружить потерю точности из-за округления как при сложении с плавающей запятой, так и при умножении?

  • Какая связь и разница между недостаточным потоком и проблемой, которую я описал? Является ли потеря точности только частным случаем потери точности из-за округления, когда результат округляется до нуля?

Спасибо.


person Tim    schedule 10.10.2020    source источник
comment
К тому времени, когда мантиссы выровнены для сложения, ничего из 3.14 не может быть представлено. Я не думаю, что это связано с округлением.   -  person Weather Vane    schedule 10.10.2020
comment
Обратите внимание, что 3.14 теряется в самой первой операции: godbolt.org/z/Y4GTcs   -  person Bob__    schedule 10.10.2020
comment
Вы уже читали это: docs.oracle.com/cd/ E19957-01/806-3568/ncg_goldberg.html ?   -  person Bob__    schedule 10.10.2020
comment
выражение (3.14+1e10)-1e10 равно 0,0 В общем случае это утверждение неверно. В одном выражении компилятору разрешено использовать дополнительную точность для хранения промежуточных результатов. И на самом деле, когда я печатаю результаты этого выражения, ответ равен 3,139999.   -  person user3386109    schedule 10.10.2020
comment
Что касается вопроса Как я могу обнаружить потерю точности, мой ответ: Вы этого не сделаете. Вместо этого вы разрабатываете свое программное обеспечение, чтобы избежать проблемы.   -  person user3386109    schedule 10.10.2020
comment
Является ли потеря точности только частным случаем потери точности из-за округления, --> C имеет результат имеет значение потери точности, если величина математического результата настолько мала, что математический результат не может быть представлен без экстраординарной ошибки округления в объекте указанный тип. Вы делаете так: Когда происходит недополнение?.   -  person chux - Reinstate Monica    schedule 11.10.2020
comment
Ответ на ваш первый вопрос см. в моем ответе здесь   -  person wim    schedule 11.10.2020
comment
Кросс-опубликовано: stackoverflow.com/q/64296463/781723, scicomp.stackexchange.com/q/36079/4274. Для тех, кто найдет это, вы можете найти дополнительные ответы на sccomp. Пожалуйста, не публикуйте один и тот же вопрос на нескольких сайтах.   -  person D.W.    schedule 29.11.2020
comment
Вместо того, чтобы жаловаться на воображаемые оскорбления и преследования , прекратите делать то, что вам постоянно говорят перестать делать, и люди перестанут просить вас перестать это делать. Если вам не нравятся напоминания о том, что нельзя делать перекрестные публикации, это невероятно просто: не делайте перекрестных публикаций. Вы как разгоняющийся водитель, который злится на гаишника вместо того, чтобы притормозить.   -  person jonrsharpe    schedule 30.11.2020


Ответы (1)


Хотя в математике сложение и умножение действительных чисел являются ассоциативными операциями, эти операции не ассоциативны при выполнении над типами с плавающей запятой, такими как float, из-за ограниченной точности и расширения диапазона.

Так что порядок имеет значение.

Учитывая примеры, число 10000000003.14 не может быть точно представлено как 32-битное float, поэтому результат (3.14f + 1e10f) будет равен 1e10f, ближайшему представимому числу. Конечно, вместо 3.14f + (1e10f - 1e10f) получится 3.14f.

Обратите внимание, что я использовал постфикс f, потому что в C выражение (3.14+1e10)-1e10 включает double литералов, так что результатом будет действительно 3.14 (или, что более вероятно, что-то вроде 3.14999).

Нечто подобное происходит и во втором примере, где 1e20f * 1e20f уже находится за пределами диапазона float (но не double) и последовательное умножение бессмысленно, а (1e20f * 1e-20f), выполняемое первым в другом выражении, имеет вполне определенный результат (1) и последовательное умножение дает правильный ответ.

На практике есть некоторые меры предосторожности, которые вы принимаете

  • Используйте более широкий шрифт. double лучше всего подходит для большинства приложений, если нет других требований.
  • Измените порядок операций, если это возможно. Например, если вам нужно добавить много терминов и вы знаете, что некоторые из них меньше других, начните добавлять их, а затем остальные. Избегайте вычитания чисел одного порядка. В общем, может существовать более точный способ вычисления алгебраического выражения, чем наивный (например, метод Хорнера для полиномиального вычисления).
  • Если у вас есть какие-то знания о проблемной области, вы, возможно, уже знаете, какая часть вычислений может иметь проблемные значения, и проверьте, больше (или меньше) ли они, чем некоторые пределы, прежде чем выполнять вычисление.
  • Проверьте результаты как можно скорее. Нет смысла продолжать вычисление, когда у вас уже есть бесконечное значение или NaN, или продолжать итерацию, когда целевое значение вообще не изменяется.
person Bob__    schedule 10.10.2020
comment
Является ли потеря точности только частным случаем потери точности из-за округления, когда результат округляется до нуля? Спасибо. - person Tim; 10.10.2020
comment
@Tim Нет, потеря значимости происходит, когда результирующее значение слишком мало для представления типом, оно выходит за пределы допустимого диапазона. Округление — это вопрос точности, оно включает в себя количество битов, зарезервированных в типе для мантиссы, а не диапазон экспоненты. Особым случаем являются субнормальные числа. - person Bob__; 10.10.2020
comment
Я не уверен, что понимаю их разницу. - person Tim; 10.10.2020
comment
@Tim Насколько мне известно, недополнение - это отрицательное переполнение экспоненты, ошибки округления возникают из-за ограниченного количества битов, зарезервированных для мантиссы. - person Bob__; 10.10.2020
comment
Я спросил о том, как определить, возникает проблема или нет, а не о том, как избежать ее возникновения. Я ищу такие ответы, как stackoverflow.com/questions/15655070/, которое предназначено для обнаружения переполнения и недополнения с плавающей запятой. Но вы сказали, что недолив и проблема в моем посте - это разные проблемы. - person Tim; 11.10.2020
comment
@Tim В моем последнем пункте я предложил проверить результат. В моем первом комментарии к вашему вопросу я связал фрагмент, который показывает ровно одну из этих возможных проверок в случае суммы (если x + y == x, когда и x, и y отличны от нуля, это означает, что y слишком мал и будет потерян) . Ответ, который вы упомянули, говорит: вы можете просто выполнять операции, а затем использовать isfinite или isinf для результатов, я также упоминаю NaN. Альтернативой, если она поддерживается вашим компилятором, являются макроконстанты в файле ‹fenv.h. ›. - person Bob__; 11.10.2020
comment
Спасибо. если вам нужно добавить много терминов и вы знаете, что некоторые из них меньше других, начните добавлять их, а затем другие. Избегайте вычитания чисел одного порядка. Почему нельзя вычитать числа одного порядка? (тогда как добавить числа того же порядка)? - person Tim; 17.10.2020
comment
@Tim Это связано с потеря значимости, которая ведет себя по-разному для двух операций. - person Bob__; 17.10.2020