Как работает этот алгоритм аппроксимации деления?
Я работаю над игрой с программным рендерером, чтобы получить наиболее точный вид PS1. Когда я занимался исследованиями того, как работала система графики/рендеринга PS1, причина шатких вершин и т. д., я наткнулся на некоторую документацию о том, как они делали свое разделение. Вот ссылка на него:http://problemkaputt.de/psx-spx.htm#gteoverview (см. раздел "неточность деления GTE")
соответствующий код:
if (H < SZ3*2) then ;check if overflow
z = count_leading_zeroes(SZ3) ;z=0..0Fh (for 16bit SZ3)
n = (H SHL z) ;n=0..7FFF8000h
d = (SZ3 SHL z) ;d=8000h..FFFFh
u = unr_table[(d-7FC0h) SHR 7] + 101h ;u=200h..101h
d = ((2000080h - (d * u)) SHR 8) ;d=10000h..0FF01h
d = ((0000080h + (d * u)) SHR 8) ;d=20000h..10000h
n = min(1FFFFh, (((n*d) + 8000h) SHR 16)) ;n=0..1FFFFh
else n = 1FFFFh, FLAG.Bit17=1, FLAG.Bit31=1 ;n=1FFFFh plus overflow flag
Я с трудом понимая, как это работает, что такое таблица "unr"? почему мы все меняем? Если бы кто-то мог дать более подробное объяснение того, как эта вещь на самом деле достигает разрыва, это было бы оценено.
1 ответов
этот алгоритм представляет собой фиксированное деление двух беззнаковых 16-битных дробных значений в [0,1). Сначала он вычисляет начальное 9-битное приближение к реципрокному делителю через поиск таблицы, уточняет это с помощью одной итерации Ньютона-Рафсона для реципрокного, xi+1: = xя * (2-d * xя), в результате чего взаимная точность около 16 бит, наконец, умножает это на дивиденд, давая 17-битный коэффициент в [0,2).
для поиска таблицы делитель сначала нормализуется в [0.5, 1), применяя масштабный коэффициент 2z, очевидно, дивиденды затем должны быть скорректированы на тот же масштабный коэффициент. Поскольку реципрокность операндов в [0.5, 1) будет [1,2], целочисленный бит реципрокности, как известно, равен 1, поэтому можно использовать 8-битные записи таблицы для получения 1,8 фиксированной точки реципрокности путем добавления 0x100
(= 1). Причина 0x101
используется здесь не ясно; это может быть из-за требования, что этот шаг всегда обеспечивает завышение истинного реципрокного.
следующие два шага являются дословными переводами итерации Ньютона-Рафсона для взаимного учета коэффициентов шкалы с фиксированной точкой. Так что 0x2000000
представляет 2.0. Код использует 0x2000080
так как он включает в себя константу округления 0x80
(=128)для следующего деления на 256, используемого для масштабирования результата. Следующий шаг также добавляет 0x00000080
как константа округления для a перемасштабирование деления на 256. Без масштабирования это было бы чистым умножением.
окончательное умножение n*d
умножает взаимное в d
С дивидендов в n
уступить фактору в 33 бита. Опять же, константа округления 0x8000 применяется перед разделением на 65536 для масштабирования в надлежащий диапазон, давая коэффициент в формате фиксированной точки 1.16.
непрерывное масштабирование типично для вычислений с фиксированной точкой, где один пытается сохранить промежуточные результаты как можно увеличить точность конечного результата. Что немного необычно, так это то, что округление применяется во всей промежуточной арифметике, а не только на последнем шаге. Возможно, необходимо было достичь определенного уровня точности.
функция не совсем точна, хотя, вероятно, вызвана неточностью в начальном приближении. Во всех не исключительных случаях 2,424,807,756 соответствуют правильно округленной 1,16 фиксированной точке результат, 780,692,403 имеют ошибку 1 ulp, 15,606,093 имеют ошибку 2-ulp, и 86,452 имеют ошибку 3-ulp. В быстром эксперименте максимум относительные ошибка в начальном приближении u
было 3.89 e-3. Улучшенный поиск таблицы уменьшает максимальную относительную ошибку в u
до 2.85 e-3, уменьшающ но не исключающ ошибки 3 ulp в окончательном результате.
если вы хотите посмотреть на конкретный пример, рассмотрите h
=0.3 (0x4ccd
) разделить на SZ3
=0.2 (0x3333
). Тогда z
=2, таким образом d
=0.2*4 = 0.8 (0xcccc
). Это приводит к u
= 1.25 (0x140
). Поскольку оценка довольно точна, мы ожидаем, что (2 - d * u) будет около 1, и на самом деле,d
= 1.000015 (0x10001
). Утонченный реципрокный выходит на d
=1.250015 (0x14001
), и частное, поэтому n
=1.500031 (0x18002
).