TL: DR использовать lrint(x)
или (int)rint(x)
для преобразования из float в int с округлением до ближайшего, а не усечения. К сожалению, не все компиляторы эффективно используют одни и те же математические функции. См round() for float in C++
gcc -mno-sse2
должен использовать x87 для double
, даже в 64-битном коде. Регистры x87 имеют внутреннюю точность 80 бит, но SSE2 использует формат IEEE binary64 (aka double
) изначально в XMM-регистрах, поэтому все временные области округлены до 64-разрядных double
на каждом шаге.
Проблема не такая интересная, как the double rounding problem (80 бит -> 64 бит, а затем целое число). Это также не от gcc -O0
(по умолчанию: без дополнительных оптимизаций) округление при хранении временных данных в памяти, потому что вы сделали все это в одном выражении C, поэтому он просто использует регистры x87 для всего выражения.
Это просто, что 80-битная точность приводит к результату, который чуть ниже 242,0 и усекается до 241 от Кассиопеяна float-> Int семантики, в то время как SSE2, дает результат чуть выше 242,0, которая обрежет до 242. Для x87 округление до следующего нижнего целого происходит последовательно, а не только 242 для любого ввода от 1 до 65535. (Я сделал версию вашей программы, используя atoi(argv[1])
, чтобы я мог тестировать другие значения и с -O3
).
Помните, что int foo = 123.99999
равно 123, потому что C использует режим округления округления (к нулю). Для неотрицательных чисел это то же самое, что и floor
(который округляется к -Infinity). https://en.wikipedia.org/wiki/Floating-point_arithmetic#Rounding_modes.
double
не может представлять коэффициенты точно: я напечатал их с gdb
и получил: {0.21260000000000001, 0.71519999999999995, 0.0722}
. Эти десятичные представления, вероятно, не являются точными представлениями значений с плавающей запятой base-2. Но они достаточно близки, чтобы увидеть, что коэффициенты составляют до 0.99999999999999996
(с использованием калькулятора произвольной точности).
Мы получаем совпадение, поскольку внутренняя точность x87 выше, чем точность коэффициентов, поэтому ошибки округления суммы в n * rec709_luma_coeff[0]
и т. Д. И, суммируя результаты, составляют ~ 2^11
меньше, чем разница между суммой коэффициентов и 1,0. (64-битное значение против 53 бит).
Реальный вопрос: как удалось работать версия SSE2! Предположительно от раунда до ближайшего, даже во временном, в достаточном количестве случается движение вверх, по крайней мере, на 242. Это случается, чтобы произвести исходный ввод для большего количества случаев, чем нет, но он производит вход-1 для 5, 7, 10, 13, 14, 20 ... (252 из первых 1000 чисел от 1..1000 являются «потеряются» по версии SSE2, так что это не так, как он всегда работает либо.)
с -O3
для вашего источника, он выполняет расчет во время компиляции с расширенной точностью и дает точный результат. то есть он компилирует то же, что и printf("%u\n", n);
.
И BTW вы должны использовать static
const
для вас констант так НКА может оптимизировать лучше. static
намного лучше, чем простой глобальный, хотя, поскольку компилятор может видеть, что ничто в компиляторе не записывает значения или не передает их адрес нигде, поэтому он может обрабатывать их так, как если бы они были const
.
Это не имеет никакого отношения к оптимизации. Нет SSE2 означает использование старого x87 FPU, который ** шире **, чем SSE2. В некотором смысле, результаты x87 выполняются с большей точностью, но результаты могут отличаться от результатов, сделанных с использованием SSE2 –
Странно, при компиляции с флагом '-O2' проблема исчезает ... – perror
Если вы включите оптимизацию, вы получите также 242 с '-mno-sse2' – jofel