Ответ 1
Да, interol_80() безопасен, пусть демонстрирует его.
Проблема заключается в том, что входы имеют 64 байта float
rnd64(ui) = ui
Результат точно (предполагается, что * и + являются математическими операциями)
r = u2*(1-u1)+(u1*u3)
Оптимальное возвращаемое значение, округленное до 64-битного поплавка,
r64 = rnd64(r)
Так как мы имеем эти свойства
u2 <= r <= u3
Гарантируется, что
rnd64(u2) <= rnd64(r) <= rnd64(u3)
u2 <= r64 <= u3
Преобразование в 80 бит u1, u2, u3 также является точным.
rnd80(ui)=ui
Теперь предположим 0 <= u2 <= u3
, тогда выполнение с неточными операциями float приведет к не более 4 ошибкам округления:
rf = rnd(rnd(u2*rnd(1-u1)) + rnd(u1*u3))
Предполагая округление до ближайшего четного, это будет не более 2 ULP с точным значением. Если округление выполняется с 64-битным поплавком или с поплавком 80 бит:
r - 2 ulp64(r) <= rf64 <= r + 2 ulp64(r)
r - 2 ulp80(r) <= rf80 <= r + 2 ulp80(r)
rf64
может быть отключен на 2 ulp, поэтому интерполь-64() небезопасен, но как насчет rnd64( rf80 )
?
Мы можем сказать, что:
rnd64(r - 2 ulp80(r)) <= rnd64(rf80) <= rnd64(r + 2 ulp80(r))
Так как 0 <= u2 <= u3
, то
ulp80(u2) <= ulp80(r) <= ulp80(r3)
rnd64(u2 - 2 ulp80(u2)) <= rnd64(r - 2 ulp80(r)) <= rnd64(rf80)
rnd64(u3 + 2 ulp80(u3)) >= rnd64(r + 2 ulp80(r)) >= rnd64(rf80)
К счастью, как и каждое число в диапазоне (u2-ulp64(u2)/2 , u2+ulp64(u2)/2)
, мы получаем
rnd64(u2 - 2 ulp80(u2)) = u2
rnd64(u3 + 2 ulp80(u3)) = u3
так как ulp80(x)=ulp62(x)/2^(64-53)
Таким образом, получаем доказательство
u2 <= rnd64(rf80) <= u3
Для u2 <= u3 <= 0 мы можем легко применить те же доказательства.
Последний исследуемый случай равен u2 <= 0 <= u3. Если мы вычтем 2 большие значения, результат может быть до ulp (большой)/2, а не ulp (big-large)/2...
Таким образом, это утверждение, которое мы сделали, больше не выполняется:
r - 2 ulp64(r) <= rf64 <= r + 2 ulp64(r)
К счастью, u2 <= u2*(1-u1) <= 0 <= u1*u3 <= u3
, и это сохраняется после округления
u2 <= rnd(u2*rnd(1-u1)) <= 0 <= rnd(u1*u3) <= u3
Таким образом, поскольку добавленные величины имеют противоположный знак:
u2 <= rnd(u2*rnd(1-u1)) + rnd(u1*u3) <= u3
то же самое происходит после округления, поэтому мы можем еще раз гарантировать
u2 <= rnd64( rf80 ) <= u3
КЭД
Чтобы быть полным, мы должны заботиться о денормальных входах (постепенном переполнении), но я надеюсь, что вы не будете настолько порочны стресс-тестами. Я не буду демонстрировать, что с ними происходит...
ИЗМЕНИТЬ
Вот продолжение, поскольку следующее утверждение было немного приближенным и вызвало некоторые комментарии, когда 0 <= u2 <= u3
r - 2 ulp80(r) <= rf80 <= r + 2 ulp80(r)
Мы можем записать следующие неравенства:
rnd(1-u1) <= 1
rnd(1-u1) <= 1-u1+ulp(1)/4
u2*rnd(1-u1) <= u2 <= r
u2*rnd(1-u1) <= u2*(1-u1)+u2*ulp(1)/4
u2*ulp(1) < 2*ulp(u2) <= 2*ulp(r)
u2*rnd(1-u1) < u2*(1-u1)+ulp(r)/2
Для следующей операции округления мы используем
ulp(u2*rnd(1-u1)) <= ulp(r)
rnd(u2*rnd(1-u1)) < u2*(1-u1)+ulp(r)/2 + ulp(u2*rnd(1-u1))/2
rnd(u2*rnd(1-u1)) < u2*(1-u1)+ulp(r)/2 + ulp(r)/2
rnd(u2*rnd(1-u1)) < u2*(1-u1)+ulp(r)
Для второй части суммы имеем:
u1*u3 <= r
rnd(u1*u3) <= u1*u3 + ulp(u1*u3)/2
rnd(u1*u3) <= u1*u3 + ulp(r)/2
rnd(u2*rnd(1-u1))+rnd(u1*u3) < u2*(1-u1)+u1*u3 + 3*ulp(r)/2
rnd(rnd(u2*rnd(1-u1))+rnd(u1*u3)) < r + 3*ulp(r)/2 + ulp(r+3*ulp(r)/2)/2
ulp(r+3*ulp(r)/2) <= 2*ulp(r)
rnd(rnd(u2*rnd(1-u1))+rnd(u1*u3)) < r + 5*ulp(r)/2
Я не подтвердил первоначальное требование, но не так далеко...