gcc CortexM4 single precision FPU: Why uses eg. 'powf' double precision functions eg '__extendsfdf2' or '__aeabi_ddiv' - cortex-m

While checking for a STM32F4 Cortex M4 with GCC compiled list file I wonder why many double precision float functions are linked, eg:
__aeabi_drsub
__aeabi_dsub
__subdf3
__aeabi_dadd
__adddf3
__floatunsidf
__aeabi_ui2d
__floatsidf
__aeabi_i2d
__aeabi_f2d
__extendsfdf2
__floatundidf
__aeabi_ul2d
__floatdidf
__aeabi_l2d
I'm using flags to avoid any double calculation:
-fsingle-precision-constant
-Wdouble-promotion
and I added 'f' behind every constants, eg '2.3f'
Is there any possibility to avoid this?

Related

Maximum Precision for C++ with Eigen3

I'm using the awesome Eigen3 library to write a MATLAB MEX file. But I am experiencing some accuracy issues (compared to MATLAB), even when using long double.
The most critical computation seems to be the one where I compute a probability according to the normal distribution.
Here is the code snippet:
p.fill( 1/( M_PIl * sigma * sigma ) );
p.array() *= ( - 0.5/pow( sigma, 2.0 ) * ( mu.array() - x.array() ).array().square() ).array().exp();
where x, p and mu are Eigen::Matrix< long double, Dynamic, 1 >. Usually these vectors have a length of 3000.
What are possible steps I can take to get the maximum possible precision?
What are the correct GCC compiler flags I can use to force 80 bit precision wherever possible?
P.S: I compile the C++ code (in MATLAB with MEX) with gcc 4.9 and my linux reports the following available instruction sets: Intel MMX, Intel SSE, Intel SSE2, Intel SSE3, Intel SSE4
Edit:
I tried what #Avi Ginsburg suggested below and compiled it using the following command:
mex -g -largeArrayDims '-I/usr/include/eigen3' CXXFLAGS='-DEIGEN_DONT_VECTORIZE -std=c++11 -fPIC' test.cpp
with double and long double and each of these options gives me the same error with respect to the solution from MATLAB.
I'm hazarding a guess here. You are using SSE instructions with your array calculations, most notably, ...array().exp(). I'm pretty sure there is no extended precision with SSE, hence the differences between MATLAB and Eigen.
By default Eigen uses a faster but slightly less accurate implementation of several mathematical functions, including the exponential. You can explicitly disable these optimizations by compiling with the -DEIGEN_FAST_MATH=0 option.
If you use gcc as your compiler also make sure that you don't use the -Ofast or -ffast-math options, as these can result in reduced precision.
If you want to compute the probability density of a (1 dimensional) normal distribution, the factor at the beginning should be 1/std::sqrt( 2* M_PIl * sigma * sigma ).
Also, the p.fill() at the beginning of your snippet is inefficient. Just write this in one line:
p = (1/std::sqrt(2*M_PIl*sigma*sigma)) *
( -0.5/(sigma*sigma) * (mu-x).array().square() ).exp();
N.B.: If you are only performing element-wise operations on your arrays, consider declaring them as Eigen::Array<...> instead of Eigen::Matrix<...>. The template parameters are the same, also the binary layout is the same, but you don't need to write .array() every time you want to make element-wise operations.

when to use iso_Fortran_env ,selected_int_kind,real(8),or -fdefault-real-8 for writing or compiling fortran code?

I have this simple code which uses DGEMM routine for matrix multiplication
program check
implicit none
real(8),dimension(2,2)::A,B,C
A(1,1)=4.5
A(1,2)=4.5
A(2,1)=4.5
A(2,2)=4.5
B(1,1)=2.5
B(1,2)=2.5
B(2,1)=2.5
B(2,2)=2.5
c=0.0
call DGEMM('n','n',2,2,2,1.00,A,2,B,2,0.00,C,2)
print *,C(1,1)
print *,C(1,2)
print *,C(2,1)
print *,C(2,2)
end program check
now when i compile this code with command
gfortran -o check check.f90 -lblas
I get some random garbage values. But when I add
-fdefault-real-8
to the compiling options I get correct values.
But since it is not a good way of variable declaration in Fortran. So I used the iso_fortran_env intrinsic module and added two lines to the code
use iso_fortran_env
real(kind=real32),dimension(2,2)::A,B,C
and compiled with
gfortran -o check check.f90 -lblas
Again I got wrong output .
Where I'm erring in this code?
I'm on 32bit linux and using GCC
DGEMM expects double precision values for ALPHA and BETA.
Without further options, you are feeding single precision floats to LAPACK - hence the garbage.
Using -fdefault-real-8 you force every float specified to be double precision by default, and DGEMM is fed correctly.
In your case, the call should be:
call DGEMM('n','n',2,2,2,1.00_8,A,2,B,2,0.00_8,C,2)
which specifies the value for alpha to be 1 as a float of kind 8, and zero of kind 8 for beta.
If you want to perform the matrix-vector product in single precision, use SGEMM.
Note that this is highly compiler-specific, you should consider using REAL32/REAL64 from the ISO_Fortran_env module instead (also for the declaration of A, B, and C).

Windows vs Linux - math result difference

I have program in C++. If I run same part of code, Linux and Windows are giving different results.
#include <cmath>
#include <cfloat>
#include <cstdio>
#define MPI 3.141592653589793238462
#define DEG_TO_RAD(x) ((x) * 0.0174532925)
#define cot(x) (1.0 / tan(x))
#define sec(x) (1.0 / cos(x))
double p1 = DEG_TO_RAD(35);
double p2 = DEG_TO_RAD(65);
double lambertN = log(cos(p1) * sec(p2));
lambertN /= (log(tan(0.25 * MPI + 0.5 * p2) * cot(0.25 * MPI + 0.5 * p1)));
double t = tan(0.25 * MPI + 0.5 * p1);
double lambertF = cos(p1) * pow(t, lambertN);
//---------------------------
//specify input coordinates in degrees
double lon = 160.25;
double lat = 245.75;
double longitude = DEG_TO_RAD(lon - 10);
double latitude = DEG_TO_RAD(lat);
double c = cot(0.25 * MPI + 0.5 * latitude);
double lambertPhi = lambertF * pow(c, lambertN);
printf("%f", lambertPhi); // here I got different results on Win and Linux
On Windows, I got correct result (or it seems so, because final result is OK).
On Linux, I got NaN or some very small numbers in comaprison to Windows.
What am I missing ?
EDIT #1:
Windows - Visual Studio 2010 - build via GUI
Linux - gcc version 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC) - built with makefile, flags: CFLAGS = -lm -lstdc++ -Wall -O2
Both systems are 64bit
PS:
If anyone is interested, this is part of Lambert-Conic projection equation.
First, there is no real reason to expect the same results,
unless you take active steps to ensure specific results. The
C++ language definition allows intermediate results to use
extended precision. Typically, if a compiler does this (and
doing it is very frequent on an Intel architectures), The
extended precision will be truncated to standard double
precision when the compiler stores to memory. And when it
stores to memory will depend on the internals of the compiler
(and probably even on the degree of optimization).
In the case of Intel, modern chips contain several floating
point processors: the older FPU uses extended precision, where
as the newer SSE variants don't. But the newer SSE variants
aren't available on older processors. By default, g++ (the
Linux compiler) uses the older FPU, to work everywhere, but
Visual C++, as far as I can tell, uses SSE. This means that by
default, you will get different results. Both compilers have
extensive options for changing this, but if you're running the
default configuration, I would not expect g++ and Visual to
give the same results.
I get nans for lat values between 90 and 270. That seems reasonable because in that range the cot() will return a negative number which later cannot simply be raised to a fractional power with pow.
The question remains why you get different results on Windows for this. But unless you provide information about the concrete input values I cannot say more.

CUDA Check out nvcc "-arch"-flag during run time

Is there somehow a possibility to call different kernels depending on whether for example nvcc -arch=sm_11 or nvcc -arch=sm_20 has been used to compile the code? To be a bit more explicit:
if (FANCY_FLAG == CU_TARGET_COMPUTE_11)
// Do some conversions here..
krnl1<<<GRID_DIM1, BLOCK_DIM1>>>(converted_value1);
else if (FANCY_FLAG == CU_TARGET_COMPUTE_20)
krnl2<<<GRID_DIM2, BLOCK_DIM2>>>(value1);
As you can see I found the CUjit_target_enum in cuda.h but I wasn't able to find out whether the nvcc defines any flags which would be equal to one of the enums values.
My intention for this is that I don't know whether my device support double precision floats or not. That would mean I have to convert my data from double to float and hence, run a different kernel (Yes, I'd prefer to run the kernel with double precision over single precision wherever possible).
I'd also appreciate a completely different approach as long as it does the trick.
In the device code check CUDA_ARCH macro value
In the host code - check major and minor fields of the device properties.

GCC equivalent to VC's floating point model switch?

Does GCC have an equivalent compiler switch to VC's floating point model switch (/fp)?
In particular, my application benefits from compiling with /fp:fast and precision is not a big deal, how should I compile it with GCC?
Try -ffast-math. On gcc 4.4.1, this turns on:
-fno-math-errno - Don't set errno for single instruction math functions.
-funsafe-math-optimizations - Assume arguments and result of math operations are valid, and potentially violate standards
-ffinite-math-only - Assume arguments and results are finite.
-fno-rounding-math - Enable optimizations that assume default rounding. This is the default, but it could be overridden by something else.
-fno-signaling-nans - Enable optimizations that can change number of math exceptions.; also default
-fcx-limited-range - Assume range reduction is not needed for complex number division:
__FAST_MATH__ macro.
You could also enable these individually.