Здравствуйте, Went, Вы писали:
W>Здравствуйте. Есть такой код: W>
W>float next = nextafterf(0.0f, 1.0f);
W>bool crazy = next == 0.0f;
W>
W>На всех платформах, с которыми я имел дело ранее, равенство ожидаемо было ложным. Но когда я собираю свежим XCode на довольно древний iPad (arm v7, по-моему), то здесь это сравнение даёт истину! В чем ошибка? Может, какие-то опции компиляции нужно покрутить? Дебаг-конфигурация, оптимизации отключены. Или число 1.40129846E-45 какое-то неправильное и процессор имеет право трактовать его как угодно?
Если взять такой код и собрать его с помощью clang/gcc с флагом -ffast-math,
то напечатает 'crazy 1'
#include <cstdio>
#include <cmath>
int main()
{
float next = nextafterf(0.0f, 1.0f);
bool crazy = next == 0.0f;
printf("crazy %d\n", crazy);
return 0;
}
Re[3]: [arm] Почему nextafterf(0.0f, 1.0f) == 0.0f?
Здравствуйте, Went, Вы писали:
W>Здравствуйте снова. W>По-моему, дело в том, что ARM использует математический сопроцессор NEON, который просто срезает все денормализированные числа в ноль. Чудненько.
Возможно кто-то выставляет флаг "flushing denormals/underflow to zero"?
Например с gcc/linux/amd64 вот так работает и без -ffast-math:
#include <cstdio>
#include <cmath>
#include <pmmintrin.h>
int main()
{
auto mxcsr = _mm_getcsr();
// Denormals & underflows are flushed to zero
mxcsr |= (1 << 15) | (1 << 6);
// All exceptions are masked
mxcsr |= ((1 << 6) - 1) << 7;
_mm_setcsr(mxcsr);
float next = nextafterf(0.0f, 1.0f);
bool crazy = next == 0.0f;
printf("crazy %d\n", crazy);
return 0;
}
Здравствуйте, Went, Вы писали:
Z>>И там тоже есть подобный регистр, достаточно кому-то в него что-то записать и все будет печально. W>Да, именно так. Но тут я уже ничего изменить не смогу. Видимо, нужно делать свою реализацию nextafterf, которая в случае ARM проверяет аргумент на ноль и вместо денормализированого числа возвращает std::numeric_limits<float>::min(). Или, вообще, делать так для всех архитектур?
А почему просто не вызвать "DisableFZ" в начале работы программы?
float next = nextafterf(0.0f, 1.0f);
bool crazy = next == 0.0f;
На всех платформах, с которыми я имел дело ранее, равенство ожидаемо было ложным. Но когда я собираю свежим XCode на довольно древний iPad (arm v7, по-моему), то здесь это сравнение даёт истину! В чем ошибка? Может, какие-то опции компиляции нужно покрутить? Дебаг-конфигурация, оптимизации отключены. Или число 1.40129846E-45 какое-то неправильное и процессор имеет право трактовать его как угодно?
W>На всех платформах, с которыми я имел дело ранее, равенство ожидаемо было ложным. Но когда я собираю свежим XCode на довольно древний iPad (arm v7, по-моему), то здесь это сравнение даёт истину! В чем ошибка? Может, какие-то опции компиляции нужно покрутить? Дебаг-конфигурация, оптимизации отключены. Или число 1.40129846E-45 какое-то неправильное и процессор имеет право трактовать его как угодно?
Further, it disables signed zero (code assumes signed zero does not exist, even if the target supports it) and rounding math, which enables among other things constant folding at compile-time.
Errare humanum est
Re[2]: [arm] Почему nextafterf(0.0f, 1.0f) == 0.0f?
Здравствуйте, Zhendos, Вы писали: Z>Если взять такой код и собрать его с помощью clang/gcc с флагом -ffast-math,
Но никаких таких флагов я не устанавливал... В XCode есть такая настройка Relax IEEE Compliance, которая вроде бы этим и заведует, обозначается как GCC_FAST_MATH, но она стоит No. Так же, как и любые оптимизации вообще. Это Debug-сборка. Если кто-то понимает в ARM-ассемблере, могу скинуть получающийся из кода:
float next = nextafterf(0.0f, 1.0f);
bool crazy = next == 0.0f;
Здравствуйте.
Поразбиравшись далее, я выяснил, что результат выполнения nextafterf(0.0f, 1.0f) (число 1.40129846E-45) — денормализированное, и каждая конкретная аппаратная реализация плавающих чисел может поддерживать, а может и не поддерживать корректную работу с ними. Отсюда возникает вопрос: что курили разработчики iOS, когда написали реализацию функции, которая явно, в простейшем случае, возвращает число, с которым их процессор работает некорректно?
Re[2]: [arm] Почему nextafterf(0.0f, 1.0f) == 0.0f?
Здравствуйте снова.
По-моему, дело в том, что ARM использует математический сопроцессор NEON, который просто срезает все денормализированные числа в ноль. Чудненько.
Re[4]: [arm] Почему nextafterf(0.0f, 1.0f) == 0.0f?
Здравствуйте, Zhendos, Вы писали:
Z>Возможно кто-то выставляет флаг "flushing denormals/underflow to zero"? Z>И там тоже есть подобный регистр, достаточно кому-то в него что-то записать и все будет печально.
Да, именно так. Но тут я уже ничего изменить не смогу. Видимо, нужно делать свою реализацию nextafterf, которая в случае ARM проверяет аргумент на ноль и вместо денормализированого числа возвращает std::numeric_limits<float>::min(). Или, вообще, делать так для всех архитектур?
Re[6]: [arm] Почему nextafterf(0.0f, 1.0f) == 0.0f?
Здравствуйте, Zhendos, Вы писали: Z>А почему просто не вызвать "DisableFZ" в начале работы программы?
Ну, во-первых, могу ли я быть уверен, что его потом кто-то не включит внезапно? А во-вторых, не приведет ли это к просадкам производительности, ведь мне эти все денормалы, по сути, не нужны? Мне лишь нужна гарантия, что nextafterf(x) > x.
Z>https://stackoverflow.com/questions/7346521/subnormal-ieee-754-floating-point-numbers-support-on-ios-arm-devices-iphone-4
Спасибо за наводку!