Разделить нижний поток

Почему потеря значимости при делении возникает только тогда, когда делитель намного меньше делимого, разве это не должно происходить в любое время, когда знаменатель достаточно близок к нулю, независимо от размера дивиденда?


person David    schedule 13.01.2012    source источник


Ответы (1)


Из http://www.strw.leidenuniv.nl/docs/intel/f_ug/ieee_ovw.htm

Исключение потери значимости возникает, если округленный результат имеет показатель степени, который слишком мал для представления с использованием формата результата с плавающей запятой.

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

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

Если числитель и знаменатель очень близки по значению, даже если они очень малы, можно получить полезный результат, поэтому очень маленький числитель не обязательно вызывает потерю значимости.

Пример:

В C# эпсилон равен 1.401298E-45.

эпсилон/эпсилон == 1.0f

Несмотря на то, что числитель очень и очень мал, результат все равно является действительным числом с плавающей запятой.

Теперь, если бы вы попробовали что-то вроде этого:

float max = 3.40282347E+38f;

/// underflow, denominator will be 0.0f
float denominator = epsilon / max; 

denominator будет иметь заказ 1e-83. Поскольку 83 намного превышает максимальный показатель числа с плавающей запятой одинарной точности, значение будет обнулено. Вот где происходит недолив.

/// generates a divide-by-zero error.
float result = 10 / denominator; 

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

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

Например, снова на C#:

10f / float.Epsilon / float.MaxValue

также как и

(10f / float.Epsilon) / float.MaxValue

дает 20971522.0f.

Однако математически эквивалентное выражение:

10f / (float.Epsilon / float.MaxValue)

дает Бесконечность.

person 3Dave    schedule 13.01.2012
comment
Я думаю, что я все еще что-то упускаю. Используя числа, не будет ли верным следующее: Предположим, что 0,001 находится за пределами диапазона и = 0; 1/.001 = 1/0 = разделить на ноль, .001/.001 = 0/0 = разделить на ноль, .01/.001 = .01/0 = разделить на ноль. Какая разница? - person David; 13.01.2012
comment
1/0,001 == 1000; по мере уменьшения знаменателя результат приближается к бесконечности, а не к нулю. Может я неправильно понимаю вопрос? - person 3Dave; 13.01.2012
comment
Скорее всего, я чего-то не понимаю в разделении нижнего потока. Я предполагаю, что компьютер не может обрабатывать 0,001 и округляет его до 0, т.е. 0,001=0, поэтому, если я попытаюсь разделить 1/001, поскольку компьютер видит 0,001 как 0, это то же самое, что деление 1/0, что будет ошибкой (независимо от того, какой числитель). Разве это не правда? - person David; 13.01.2012
comment
Я думаю, что начинаю понимать. Примеры действительно помогают. Просто чтобы уточнить, это второй раунд делений, верно? Это означает, что все это выглядит как 10/(эпсилон/макс) == разделить недостаточный поток == разделить на ноль. - person David; 13.01.2012
comment
@David Если вы передадите ненулевое значение, например 0,001, это значение будет использоваться в немедленном расчете с двумя членами. Поскольку делитель равен 0,001, вы не получите деление на ноль, так как это не ноль. ЭТОТ результат может быть зажат до нуля, вызывая потерю значимости, и если этот ноль затем используется в качестве делителя, вы получите ошибку деления на ноль. Однако деление на эпсилон (0,001 в вашем примере) никогда не должно сразу вызывать ошибку деления на ноль. - person 3Dave; 13.01.2012
comment
@ Дэвид, да, это звучит правильно. Разница в том, в каком порядке выполняются операции. Если промежуточный результат превышает допустимую точность, вы получаете ошибки. В противном случае нет. - person 3Dave; 13.01.2012