asin
реализован в libm
, который является частью стандартной библиотеки C, а не стандартной библиотеки C ++.(Технически стандартная библиотека C ++ включает функции библиотеки C, но на практике реализации библиотек C ++ Gnu и LLVM полагаются на базовую математическую библиотеку платформы.) Три платформы - Linux, OS X и Windows - каждая имеет свою собственнуюреализация математической библиотеки, поэтому, если бы использовалась библиотечная функция, это, безусловно, могла бы быть другая библиотечная функция, и результат мог бы отличаться в последней битовой позиции (что показывает ваш тест).
Однако,вполне возможно, что библиотечная функция никогда не вызывается во всех случаях.Это будет зависеть от компиляторов и опций оптимизации, которые вы им передаете (и, возможно, некоторых других опций).Поскольку функция asin
является частью стандартной библиотеки и, следовательно, имеет известное поведение, для компилятора вполне законно вычислять значение std::asin(-1.0F)
во время компиляции, как это было бы для любого другого константного выражения (например, 1.0 + 1.0
).(который почти любой компилятор будет постоянно сворачивать до 2.0
во время компиляции).
Поскольку вы не упоминаете, какие настройки оптимизации вы используете, трудно точно сказать, что происходит, но я сделалнесколько тестов с http://gcc.godbolt.org, чтобы получить основную идею:
GCC константа сворачивает вызов к asin
без каких-либо флагов оптимизации, но это делаетне предварительно вычислять продвижение аргумента в printf
(который преобразует a
в double
, чтобы передать его printf
), если вы не укажете по крайней мере -O1
.(Протестировано с GCC 8.3).
Clang (7.0) вызывает стандартную библиотечную функцию, если вы не укажете хотя бы -O2
.Тем не менее, если вы явно вызываете asinf
, он постоянно сгибается в -O1
.Go figure.
MSVC (v19.16) не является постоянным сгибом.Он либо вызывает оболочку std::asin
, либо напрямую вызывает asinf
, в зависимости от настроек оптимизации.Я не совсем понимаю, что делает обертка, и я не тратил много времени на изучение.
И константа GCC, и Clang сворачивают выражение в одно и то же двоичное значение (0xBFF921FB60000000 какdouble), что является двоичным значением -1.10010010000111111011011 (конечные нули обрезаются).
Обратите внимание, что существует также разница между реализациями printf
на трех платформах (printf
также является частью платформыС библиотека).Теоретически, вы могли видеть различные десятичные выходные данные из того же двоичного значения, но, поскольку аргумент printf
повышен до double
до вызова printf
, и повышение точно определено и не изменяет значение, этоКрайне маловероятно, что это окажет какое-либо влияние в данном конкретном случае.
В качестве дополнительного примечания: если вы действительно заботитесь о седьмой десятичной запятой, используйте double
вместо float
.В самом деле, вы должны использовать float
только в очень специфических приложениях, в которых точность не важна;нормальный тип с плавающей запятой double
.