Как имитировать одноточечное округление с удвоением? - программирование
Подтвердить что ты не робот

Как имитировать одноточечное округление с удвоением?

У меня была проблема, когда я пытался восстановить формулу, используемую в существующей системе, довольно простая формула одного входа и одного выхода:

y = f(x)

После многого недоумения нам удалось выяснить формулу, которая соответствует нашим наблюдаемым точкам данных:

enter image description here

И как вы можете видеть, наша теоретическая модель очень хорошо подходит к наблюдаемым данным:

enter image description here

За исключением случаев, когда мы строим остаточные ошибки (т.е. y = f(x) - actualY), мы видим, что в остатках появляются строки:

enter image description here

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

  • с 8-битной точностью фракции
  • используя округлую модель 0,5 округления:

Мы могли бы имитировать эту 8-битную точность во фракции:

multiply by 128 (i.e. 2^8)
apply the round
divide by 128 (i.e. 2^8)

Изменение нашего уравнения выше:

enter image description here

Это значительно уменьшает остаточные ошибки:

enter image description here

Теперь все вышеизложенное не имеет отношения к моему вопросу, кроме:

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

Теперь я хочу симулировать числа с плавающей запятой Single Precision, внутри языка программирования (и Excel), которые используют числа с плавающей запятой Double Precision. я хочу сделать это, потому что я думаю, это то, что нужно.

В приведенном выше примере я мысль исходная система использовала Decimal data type with fixed 8-bit fractional precision using 0.5 round-up rules. Затем мне пришлось найти способ имитировать эту вычислительную модель с помощью Double math. Теперь я думаю, что исходная система использует математику точности Single, которую я хочу имитировать с помощью Double.

Как имитировать одноточечное округление с использованием удвоений?

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

enter image description here

Проблема заключается в том, что ошибка становится больше и только видна, поскольку мои входные переменные становятся больше. я понял, что это, вероятно, связано с тем, что все числа с плавающей запятой нормированы на IEEE 754 "научная нотация".

И даже если я ошибаюсь, я все же хочу попробовать.

И даже если я не хочу его пытаться, я все еще задаю вопрос

Как смоделировать округление точности Single с помощью Doubles?


Мне кажется, я все еще мог применить понятие "округление после 8 дробных бит" (хотя 24 бит для точности Single с плавающей запятой), пока я могу сначала "нормализовать" значение. например.

1234567898.76543

необходимо преобразовать в (что-то похожее):

1.23456789876543 E-09

Затем я мог применить свой "круглый к 24-му биту" (т.е. 2 ^ 24 = 16 777 216)

floor(1.23456789876543E-09 * 16777216 + 0.5) / 16777216;

Таким образом, проблема заключается в том, какая комбинация sign, abs, ln, exp (или других функций) может применяться, чтобы я мог "нормализовать" мое значение, округлить его до n-м двоичное место, затем "denormalize" это?

Примечание: я понимаю, что представление IEEE хранит двоичный 1 как самый старший бит. мне может не понадобиться дублировать это поведение, чтобы получить правильные результаты. Таким образом, это не прерыватель транзакций, и не следует предполагать, что весь подход является провалом.

См. также

4b9b3361

Ответ 1

Вы хотите использовать библиотечные функции frexp и ldexp, которые являются стандартными функциями C99 и доступны в Lua.

frexp берет число с плавающей запятой и отделяет мантиссу от экспоненты. Полученная мантисса равна либо 0, либо в одном из диапазонов [0,5, 1,0) или (-1,0, 0,5]. Вы можете затем удалить любые лишние биты очевидным образом (floor(mantissa * 2^k)/2^k для неотрицательных значений, например). (Отредактировано для добавления:) Лучше вычесть k из экспоненты в вызове ldexp, чем делать деление, как показано, потому что я уверен, что Lua не гарантирует точность 2 ^ k.

ldexp - это инверсия frexp; вы можете использовать это, чтобы снова вернуть укороченное число.

Я не знаю, как это сделать в Excel. Проверьте руководство:) (Отредактировано для добавления:) Я полагаю, вы могли бы получить примерно такой же эффект, разделив число на 2 на мощность потолка логарифма 2 числа, а затем выполнив двоичный раунд, как указано выше, а затем реверсирование процесса для воссоздания исходного показателя. Но я подозреваю, что результаты будут иногда сталкиваться с особенностями с особыми идеями Excel об арифметике.

Ответ 2

Вы можете получить большую часть эффекта округления до одинарной точности, используя:

y = x + x * 0x1p29 - x * 0x1p29;

В большинстве случаев это приводит к тому же результату в y, как если бы x был округлен до float (32-разрядный двоичный IEEE 754), а затем преобразован обратно в двойной (64-разрядный). Он работает, добавляя значение (x * 0x1p29), которое "выталкивает" некоторые биты x из знака, вызывая округление в бите 23, а затем вычитая добавленное значение. (0x1p29 представляет собой шестнадцатеричную плавающую точку для 2 29 536870912.)

В редких случаях он производит несколько иной результат. Если вы просто хотите уменьшить шум в модели, эти редкие случаи могут быть незначительными. Если вы хотите их устранить, вместо того, чтобы добавлять и вычитать 2 29 x, вы можете найти наибольшую степень 2 не больше x и добавить и вычесть 2 29 что вместо 2 29 x. (Чтобы найти мощность 2, вы можете взять логарифм base-two и взять слово. Однако все еще есть проблемы округления, которые могут потребовать компенсации. Кроме того, если вход может быть нулевым или отрицательным, вы должны избегать ошибка, возникающая при выполнении логарифма.)

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

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

Ответ 3

Используйте такой код (C):

double x, y;
/ ... y gets a double value somewhere ... /
x = (double)(float)y;

После этого x (double) будет иметь значение, полученное в результате округления y как float с одинарной точностью.