Детали плавающей точки (6 стр)
Автор: FordPerfect
Стандартная библиотека
Данный раздел содержит краткий обзор частей стандартной библиотеки, относящихся к работе с плавающей точкой.
С ними имеет смысл ознакомиться, чтобы избежать переизобретения функциональности, предоставляемой библиотекой языка.
Относящимися к числам с плавающей точкой можно охарактеризовать заголовочные файлы:
<float.h>, <fenv.h>, <math.h>
а также ряд функций (strtod, strtof, strtold, fprintf, fscanf) из <stdlib.h>, <stdio.h> и <wchar.h> и обработчик SIGFPE из <signal.h>.
В C++ к ним добавляются
<limits>, и отчасти <valarray>, <numeric>, <iostream>.
Также существуют заголовки, связанные с комплексными числами:
<complex.h> - для встроенных комплексных типов языка C
<complex> - для комплексных типов C++
В C также в наличии заголовок <tgmath.h>.
Осмысленно понимать различия между <math.h> и <cmath>, которые содержат изрядно тонкостей. Ситуация дополнительно осложняется наличием <tgmath.h> и <ctgmath>.
См.
http://en.cppreference.com/w/cpp/header/cfloat - свойства и ограничения типов
http://en.cppreference.com/w/cpp/header/limits - свойства и ограничения типов
http://en.cppreference.com/w/cpp/header/cfenv - работа с исключениями и направлением округления
http://en.cppreference.com/w/cpp/header/cmath - математические функции
http://en.cppreference.com/w/cpp/header/cstdlib
http://en.cppreference.com/w/cpp/header/complex - комплексные числа
http://en.cppreference.com/w/cpp/header/cstdio
http://en.cppreference.com/w/cpp/header/istream
Опции компиляции
Visual Studio:
Используется ли FPU или SSE (или другие расширения, напр. AVX) для вычислений определяется опцией /arch:
/arch:IA32 - используется FPU
/arch:SSE - используется SSE
/arch:SSE2 - используется SSE2
/arch:AVX - используется AVX
/arch:AVX2 - используется AVX2
См. https://msdn.microsoft.com/en-us/library/7t5yh4fd.aspx .
Точность определяется опцией /fp:
/fp:
/fp:fast - режим быстрого кода, в ущерб соответствию стандарту.
/fp:precise - режим повышенной точности. Режим по умолчанию.
/fp:strict - режим максимального соответствия IEEE 754.
/fp:except - исключения происходят непосредственно по возникновении. Отключена по умолчанию. Несовместима с /fp:fast.
/fp:except- - явно отключает /fp:except.
См. https://msdn.microsoft.com/en-us/library/e7s85ffb%28v=vs.100%29.aspx .
Также Visual Studio предоставляет несколько низкоуровневых опций /Q:
/Qfast_transcendentals (принудительное использование быстрых трансцендентных функций): создает быстрые трансцендентные функции.
/QIfist (Suppress _ftol): подавляет использование функции _ftol при необходимости преобразования из типа с плавающей запятой к целому типу (только архитектура x86).
/Qimprecise_fwaits (Удалить ожидания в блоке try): удаляет команды fwait в блоках try.
/Qsafe_fp_loads: Подавляет оптимизацию для чисел с плавающей запятой с регистр и для перемещений между памятью и регистрами MMX.
См. https://msdn.microsoft.com/en-us/library/d180ccbk.aspx .
GCC:
Вычисления с плавающей точкой могут проводиться используя FPU или SSE (или другие расширения, напр. AVX). Выбор определяется опцией компиляции -fmpmath. Её возможные значения:
-mfpmath=387 - используется FPU
-mfpmath=sse - используется SSE (скалярные операции). Режим по умолчанию для x86_64.
-mfpmath=sse,387 - используются оба (эквивалентные названия -mfpmath=sse+387, -mfpmath=both)
При -mfpmath=sse сам SSE должен быть включен соответствующей опцией (-msse, -msse2, и т. д.)
Это влияет в т. ч. на соглашение о вызове (calling convention) и ABI.
В GCC есть ряд опций, контролирующий различные детали выполнения операций с плавающей точкой. Компилятор предоставляет опции для того, чтобы ускорять выполнение программы, ценой менее строгого соответствия IEEE 754.
Несколько из них объединены в общую опцию -ffast-math (включает -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans, -fcx-limited-range and -fexcess-precision=fast, определяет макро __FAST_MATH__).
Подробнее:
https://gcc.gnu.org/wiki/FloatingPointMath
https://gcc.gnu.org/wiki/x87note
https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
Clang старается имитировать GCC.
Доступ к битовому представлению
Иногда возникает желание получить доступ к битовому представлению вещественного числа. В частности, целью может быть интерпретация полученных бит как целого числа (со знаком или без). Стоит понимать, как результат зависит от endianness вещественных и целых чисел (для x86/x86_64 это не представляет затруднений, т. к. endianness жёстко зафиксирована).
Два популярных метода - каст указателя и преобразование через union - являются undefined behavior. Первое нарушает strict aliasing и на практике может приводить к проблемам. Второе обычно безопасно на практике (и разрешено в C, т. е. является undefined behavior только в C++).
Однако может возникнуть желание пользоваться методом который не нарушает Стандарт.
Рекомендуемые метод для этого следующий:
inline uint32_t float2bits(float src) { uint32_t ret; memcpy( &ret,&src,sizeof( src)); return ret; } inline float bits2float( uint32_t src) { float ret; memcpy( &ret,&src,sizeof( src)); return ret; } inline uint64_t double2bits( double src) { uint64_t ret; memcpy( &ret,&src,sizeof( src)); return ret; } inline double bits2double( uint64_t src) { double ret; memcpy( &ret,&src,sizeof( src)); return ret; }
На практике популярные компиляторы способны оптимизировать вызов memcpy, поэтому дополнительные затраты типично отсутствуют.
4 мая 2017 (Обновление: 31 дек 2018)