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 какое-то неправильное и процессор имеет право трактовать его как угодно?
Здравствуйте, 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[2]: [arm] Почему nextafterf(0.0f, 1.0f) == 0.0f?
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[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;
}
Здравствуйте, Zhendos, Вы писали:
Z>Возможно кто-то выставляет флаг "flushing denormals/underflow to zero"? Z>И там тоже есть подобный регистр, достаточно кому-то в него что-то записать и все будет печально.
Да, именно так. Но тут я уже ничего изменить не смогу. Видимо, нужно делать свою реализацию nextafterf, которая в случае ARM проверяет аргумент на ноль и вместо денормализированого числа возвращает std::numeric_limits<float>::min(). Или, вообще, делать так для всех архитектур?
Re[5]: [arm] Почему nextafterf(0.0f, 1.0f) == 0.0f?
Здравствуйте, Went, Вы писали:
Z>>И там тоже есть подобный регистр, достаточно кому-то в него что-то записать и все будет печально. W>Да, именно так. Но тут я уже ничего изменить не смогу. Видимо, нужно делать свою реализацию nextafterf, которая в случае ARM проверяет аргумент на ноль и вместо денормализированого числа возвращает std::numeric_limits<float>::min(). Или, вообще, делать так для всех архитектур?
А почему просто не вызвать "DisableFZ" в начале работы программы?
Здравствуйте, 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
Спасибо за наводку!