Я сделал функцию g
способная в некоторой степени аппроксимировать функцию, эта функция дает точные результаты с точностью до 5 десятичных знаков (1,23456xxxxxxxxxxxx, где позиции x являются просто ошибками округления / ненужными).
Чтобы избежать распространения ошибки на другие вычисления, которые будут использовать результаты g
Я хотел бы просто установить все позиции х на ноль, еще лучше, просто установить на 0 все после 5-го знака после запятой.
В литературе по X87 и SSE я не нашел ничего такого, что позволило бы мне играть с IEEE 754 битами или их отображением так, как мне бы хотелось.
Есть старая ссылка на FISTP
инструкция для X87, которая, очевидно, отражается в мире SSE с FISTTP
с той выгодой, что FISTTP
не обязательно изменяет контрольное слово и, следовательно, быстрее.
Я заметил что FISTTP
назывался «режим измельчения», но теперь в более современной литературе это просто «округление до нуля» или «усечение», и это сбивает меня с толку, потому что «измельчение» означает удаление чего-либо вообще, тогда как «округление до нуля» не обязательно означает то же самое мне .
Мне не нужно округлять до нуля, мне нужно только сохранить до 5 десятичных знаков в качестве последнего шага в моей функции перед сохранением результата в памяти; Как мне это сделать в X87 (скалярный FPU) и SSE (векторный FPU)?
Как прокомментировали несколько человек, более раннее округление не помогает точному результату. Если вы хотите узнать больше о сравнениях с плавающей запятой и странностях / ошибках, я настоятельно рекомендую серию статей Брюса Доусона по плавающей запятой. Вот цитата из тот, с индексом
Мы наконец достигли той точки в этой серии, которую я ждал
[С плавающей точкой] математика это сложно.
за. В эта почта Я собираюсь поделиться наиболее важной частью
знание математики с плавающей точкой, которое у меня есть. Вот:Вы просто не поверите, насколько это невероятно тяжело.
Я имею в виду, вы можете подумать, что трудно рассчитать, когда поезда из
Чикаго и Лос-Анджелес столкнутся, но это просто
математика с плавающей точкой
(Бонусные баллы, если вы узнаете этот последний абзац как перефразировку известной строки о космосе.)
Нет никаких машинных инструкций или стандартных библиотечных функций языка C, которые можно было бы усекать или округлять до целых чисел.
Обратите внимание, что есть машинные инструкции (и функции C), которые округляют double
до ближайшего (представимого) целого числа без преобразования его в intmax_t
или что-нибудь, просто double
->double
, Так что нет обхода через целое число дополнения фиксированной ширины 2.
Таким образом, чтобы использовать их, вы можете увеличить свое плавание на некоторый коэффициент, округлить до ближайшего целого числа, а затем уменьшить его. (как у Чукса round()
функция, но я бы порекомендовал C99 double rint(double)
вместо round()
, round
имеет странную семантику округления, которая не соответствует ни одному из доступных режимов округления в x86, поэтому он компилируется в худший код.
Упоминаемые вами инструкции x86 для asm не являются чем-то особенным, и не делайте ничего, что вы не можете попросить компилятора сделать с чистым C.
FISTP
(Float Integer STore (и Pop стек x87) — это один из способов реализации компилятором или программистом asm long lrint(double)
или же (int)nearbyint(double)
, Некоторые компиляторы делают лучший код для одного или другого. Округляется с использованием текущего режима округления x87 (по умолчанию: от округления до ближайшего), который является та же семантика, что и у стандартных функций ISO C.
FISTTP
(Float Integer STore с усечением (и извлечение стека x87) часть SSE3, хотя он работает на стеке x87. Это позволяет компиляторам избегать установки режима округления на усечение (округление к нулю) для реализации семантики усечения C: (long)x
, а затем восстановление старого режима округления.
Вот о чем говорит «не изменять управляющее слово». Ни одна инструкция не делает этого, но для реализации (int)x
без FISTTP, компилятор должен использовать Другой инструкции по изменению и восстановлению режима округления вокруг FIST
инструкция. Или просто использовать SSE2 CVTTSD2SI
преобразовать double в регистр xmm с усечением вместо значения FP в устаревшем стеке x87.
поскольку FISTTP
доступно только с SSE3, вы будете использовать его только для long double
или в 32-битном коде, который имеет значения FP в x87, так или иначе, из-за жесткого старого 32-битного ABI, который возвращает значения FP в стеке x87.
PS. если ты не узнал Брюса HHGTG ссылка, оригинал:
Пространство большое. Действительно большой. Вы просто не поверите, насколько сильно
ошеломляюще большой это. Я имею в виду, вы можете думать, что это долгий путь вниз
дорога к аптеке, но это просто арахис в космос.
Как мне это сделать в X87 (скалярный FPU) и SSE (векторный FPU)?
Следующее не использует X87, ни SSE. Я включил его в качестве ссылки сообщества для общего кода. Во всяком случае, его можно использовать для тестирования решения X87.
Любое «измельчение» результата g()
будет по крайней мере минимально увеличение ошибки, возможно, допустимое, поскольку OP сказал: «Чтобы избежать распространения ошибки на другие вычисления …»
Неясно, хочет ли OP, чтобы «точные результаты с точностью до 5 десятичных знаков» отражали абсолютную точность (+/- 0,000005) или относительную точность (+/- 0,000005 * результат). Примем «абсолютную точность».
поскольку float
, double
очень часто двоичный с плавающей точкой, любая «отбивная» будет отражать число FP ближайший кратный 0,00001.
Метод текста:
// - x xxx...xxx . xxxxx \0
char buf[1+1+ DBL_MAX_10_EXP+1 +5 +1];
sprintf(buf, "%.5f", x);
x = atof(buf);
round()
rint()
метод:
#define SCALE 100000.0
if (fabs(x) < DBL_MAX/SCALE) {
x = x*SCALE;
x = rint(x)/SCALE;
}
Прямая битовая манипуляция x
, Просто обнулите биты в значении.
TBD code.