kthohr / gcem Goto Github PK
View Code? Open in Web Editor NEWA C++ compile-time math library using generalized constant expressions
Home Page: https://gcem.readthedocs.io/en/latest/
License: Apache License 2.0
A C++ compile-time math library using generalized constant expressions
Home Page: https://gcem.readthedocs.io/en/latest/
License: Apache License 2.0
In tan_begin:
template<typename T>
constexpr
T
tan_begin(const T x)
{ // tan(x) = tan(x + pi)
return( x > T(GCEM_PI) ? \
// if
tan_begin( x - T(GCEM_PI) * (int)(x/T(GCEM_PI)) ) :
// else
tan_cf_main(x) );
}
The cast
(int)(x/T(GCEM_PI))
can overflow and lead to an infinite loop.
Building GCEM in a project using Cmake on Windows 10, and with MSVC 16 2019 as the compiler fails, it appears that the implementation of _copysign cannot be used in a constant context. Many errors were thrown of the form:
C:\Users\61405\source\repos\BGNC\extern\gcem\include\gcem_incl/neg_zero.hpp(34,31): message : see usage of '_copysign' [C:\Users\61405\source\repos\BGNC\build\tests\BGNCTestExec.vcxproj]
C:\Users\61405\source\repos\BGNC\tests\coordinates_tests\earth_tests.cpp(129,28): error C2131: expression did not evaluate to a constant [C:\Users\61405\source\repos\BGNC\build\tests\BGNCTestExec.vcxproj]
C:\Users\61405\source\repos\BGNC\extern\gcem\include\gcem_incl/neg_zero.hpp(34,31): message : failure was caused by call of undefined function or one not declared 'constexpr' [C:\Users\61405\source\repos\BGNC\build\tests\BGNCTestExec.vcxproj]
The same code compiled fine when using GCC.
I've confirmed that the errors are removed by manually implementing a version of copysign:
#gcem_options.hpp
template <typename T>
constexpr T HACK_MSVCcopysign(T x, T y)
{
if ((x < 0 && y > 0) || (x > 0 && y < 0))
{
return -x;
}
return x;
}
#ifdef _MSC_VER
#ifndef GCEM_SIGNBIT
#define GCEM_SIGNBIT(x) _signbit(x)
#endif
#ifndef GCEM_COPYSIGN
// #define GCEM_COPYSIGN(x,y) _copysign(x,y)
#define GCEM_COPYSIGN(x,y) HACK_MSVCcopysign(x, y)
#endif
...
The check for a C++ 11 compiler fails when using x86_64-w64-mingw32-g++ (Version : v11.2.1 20211019)
-- GCE-Math version 1.16.0
-- Performing Test COMPILER_SUPPORTS_CXX11
-- Performing Test COMPILER_SUPPORTS_CXX11 - Failed
CMake Error at build/_deps/gcem-src/CMakeLists.txt:50 (message):
Unsupported compiler /usr/lib64/ccache/x86_64-w64-mingw32-g++ GCEM requires
a C++11-compatible compiler.
The following code doesn't compile:
constexpr long double const a = 42e32;
constexpr auto const f = gcem::round(a);
My g++-7.4.0 gives me:
file.cpp: In function ‘int main()’:
file.cpp:49:38: in constexpr expansion of ‘gcem::round<long double>(4.20000000000000011729e+33l)’
./../include/gcem_incl/round.hpp:70:33: in constexpr expansion of ‘gcem::internal::round_check<long double>(((long double)x))’
./../include/gcem_incl/round.hpp:52:35: in constexpr expansion of ‘gcem::internal::round_int<long double>(gcem::abs<long double>(((long double)x)))’
./../include/gcem_incl/round.hpp:33:37: in constexpr expansion of ‘gcem::internal::find_whole<long double>(((long double)x))’
./../include/gcem_incl/find_whole.hpp:37:42: in constexpr expansion of ‘gcem::internal::floor_check<long double>(((long double)x))’
file.cpp:49:40: error: overflow in constant expression [-fpermissive]
constexpr auto const f = gcem::round(a);
^
file.cpp:49:40: error: overflow in constant expression [-fpermissive]
file.cpp:49:40: error: overflow in constant expression [-fpermissive]
The same is true if you replace round
with floor
or ceil
.
I think it should at least be documented that these functions will fail if the input is outside of the bounds of a long long int
. Alternatively, maybe it is possible to fall back to a different algorithm for large numbers or deduce that all numbers larger than std::numeric_limits<long long int>::max()
are integers anyway (this might at least be true for the smaller floating point types like float
, I didn't check). In the latter case, it would be fine to just return the input unmodified.
Hi, I like your library, but I cannot find any information about precision guarantees.
In my program, I need a precomputed lookup table of some angles. Before I discovered your library I computed the values at run-time and copy & pasted them into my source. After I discovered your library I wanted to assert, that my precomputed table values match constexpr computation from your library. And this test failed.
I noticed, that your computation are sometimes off by one at last significant place. So, I wanted to ask what precision do you guarantee? Is this a bug or is this (sometimes off by one at ULP) intended behavior? Or maybe it is not possible to compute trigonometric functions precisely at compile-time at all?
I created a little test program, it computes sin and cos of all angles from -15 to +15 degrees, stepping by one degree. I compiled it on Ubuntu 18.04 using GCC/G++ 10, and on Windows using Visual Studio 2019 (19.27.29112).
Best regards, Marek.
The program:
#include <cmath>
#include <cstdio>
#include <numbers>
#include <gcem.hpp>
int main()
{
static constexpr auto const deg_to_rad = [](double const& deg) -> double { return deg * (std::numbers::pi_v<double> / 180.0); };
for(int i = -15; i != 15 + 1; ++i)
{
std::printf("std::cos (%+3d deg) == %.30f (%a)\n", i, std::cos(deg_to_rad(i)), std::cos(deg_to_rad(i)));
std::printf("gcem::cos(%+3d deg) == %.30f (%a)%s\n", i, gcem::cos(deg_to_rad(i)), gcem::cos(deg_to_rad(i)), std::cos(deg_to_rad(i)) == gcem::cos(deg_to_rad(i)) ? "" : " bug");
}
for(int i = -15; i != 15 + 1; ++i)
{
std::printf("std::sin (%+3d deg) == %.30f (%a)\n", i, std::sin(deg_to_rad(i)), std::sin(deg_to_rad(i)));
std::printf("gcem::sin(%+3d deg) == %.30f (%a)%s\n", i, gcem::sin(deg_to_rad(i)), gcem::sin(deg_to_rad(i)), std::sin(deg_to_rad(i)) == gcem::sin(deg_to_rad(i)) ? "" : " bug");
}
}
Compile on Ubuntu:
g++-10 -std=c++20 gcem_trig_check.cpp
Output on Ubuntu:
std::cos (-15 deg) == 0.965925826289068312213714762038 (0x1.ee8dd4748bf15p-1)
gcem::cos(-15 deg) == 0.965925826289068312213714762038 (0x1.ee8dd4748bf15p-1)
std::cos (-14 deg) == 0.970295726275996472942608761514 (0x1.f0ca99f79ba25p-1)
gcem::cos(-14 deg) == 0.970295726275996583964911224029 (0x1.f0ca99f79ba26p-1) bug
std::cos (-13 deg) == 0.974370064785235245885530730447 (0x1.f2e0a214e870fp-1)
gcem::cos(-13 deg) == 0.974370064785235245885530730447 (0x1.f2e0a214e870fp-1)
std::cos (-12 deg) == 0.978147600733805688832944724709 (0x1.f4cfc327a008p-1)
gcem::cos(-12 deg) == 0.978147600733805577810642262193 (0x1.f4cfc327a007fp-1) bug
std::cos (-11 deg) == 0.981627183447663975712771389226 (0x1.f697d6938b6c2p-1)
gcem::cos(-11 deg) == 0.981627183447663975712771389226 (0x1.f697d6938b6c2p-1)
std::cos (-10 deg) == 0.984807753012208020315654266597 (0x1.f838b8c811c17p-1)
gcem::cos(-10 deg) == 0.984807753012208131337956729112 (0x1.f838b8c811c18p-1) bug
std::cos ( -9 deg) == 0.987688340595137770350220307591 (0x1.f9b24942fe45cp-1)
gcem::cos( -9 deg) == 0.987688340595137659327917845076 (0x1.f9b24942fe45bp-1) bug
std::cos ( -8 deg) == 0.990268068741570361979142944620 (0x1.fb046a930947ap-1)
gcem::cos( -8 deg) == 0.990268068741570473001445407135 (0x1.fb046a930947bp-1) bug
std::cos ( -7 deg) == 0.992546151641321983127852490725 (0x1.fc2f025a23e8bp-1)
gcem::cos( -7 deg) == 0.992546151641322094150154953240 (0x1.fc2f025a23e8cp-1) bug
std::cos ( -6 deg) == 0.994521895368273289861349439889 (0x1.fd31f94f867c6p-1)
gcem::cos( -6 deg) == 0.994521895368273289861349439889 (0x1.fd31f94f867c6p-1)
std::cos ( -5 deg) == 0.996194698091745545198705258372 (0x1.fe0d3b41815a2p-1)
gcem::cos( -5 deg) == 0.996194698091745434176402795856 (0x1.fe0d3b41815a1p-1) bug
std::cos ( -4 deg) == 0.997564050259824197652847033169 (0x1.fec0b7170fff6p-1)
gcem::cos( -4 deg) == 0.997564050259824197652847033169 (0x1.fec0b7170fff6p-1)
std::cos ( -3 deg) == 0.998629534754573833232882407174 (0x1.ff4c5ed12e61dp-1)
gcem::cos( -3 deg) == 0.998629534754573833232882407174 (0x1.ff4c5ed12e61dp-1)
std::cos ( -2 deg) == 0.999390827019095762118183756684 (0x1.ffb0278bf0567p-1)
gcem::cos( -2 deg) == 0.999390827019095651095881294168 (0x1.ffb0278bf0566p-1) bug
std::cos ( -1 deg) == 0.999847695156391269577511593525 (0x1.ffec097f5af8ap-1)
gcem::cos( -1 deg) == 0.999847695156391158555209131009 (0x1.ffec097f5af89p-1) bug
std::cos ( +0 deg) == 1.000000000000000000000000000000 (0x1p+0)
gcem::cos( +0 deg) == 1.000000000000000000000000000000 (0x1p+0)
std::cos ( +1 deg) == 0.999847695156391269577511593525 (0x1.ffec097f5af8ap-1)
gcem::cos( +1 deg) == 0.999847695156391158555209131009 (0x1.ffec097f5af89p-1) bug
std::cos ( +2 deg) == 0.999390827019095762118183756684 (0x1.ffb0278bf0567p-1)
gcem::cos( +2 deg) == 0.999390827019095651095881294168 (0x1.ffb0278bf0566p-1) bug
std::cos ( +3 deg) == 0.998629534754573833232882407174 (0x1.ff4c5ed12e61dp-1)
gcem::cos( +3 deg) == 0.998629534754573833232882407174 (0x1.ff4c5ed12e61dp-1)
std::cos ( +4 deg) == 0.997564050259824197652847033169 (0x1.fec0b7170fff6p-1)
gcem::cos( +4 deg) == 0.997564050259824197652847033169 (0x1.fec0b7170fff6p-1)
std::cos ( +5 deg) == 0.996194698091745545198705258372 (0x1.fe0d3b41815a2p-1)
gcem::cos( +5 deg) == 0.996194698091745434176402795856 (0x1.fe0d3b41815a1p-1) bug
std::cos ( +6 deg) == 0.994521895368273289861349439889 (0x1.fd31f94f867c6p-1)
gcem::cos( +6 deg) == 0.994521895368273289861349439889 (0x1.fd31f94f867c6p-1)
std::cos ( +7 deg) == 0.992546151641321983127852490725 (0x1.fc2f025a23e8bp-1)
gcem::cos( +7 deg) == 0.992546151641322094150154953240 (0x1.fc2f025a23e8cp-1) bug
std::cos ( +8 deg) == 0.990268068741570361979142944620 (0x1.fb046a930947ap-1)
gcem::cos( +8 deg) == 0.990268068741570473001445407135 (0x1.fb046a930947bp-1) bug
std::cos ( +9 deg) == 0.987688340595137770350220307591 (0x1.f9b24942fe45cp-1)
gcem::cos( +9 deg) == 0.987688340595137659327917845076 (0x1.f9b24942fe45bp-1) bug
std::cos (+10 deg) == 0.984807753012208020315654266597 (0x1.f838b8c811c17p-1)
gcem::cos(+10 deg) == 0.984807753012208131337956729112 (0x1.f838b8c811c18p-1) bug
std::cos (+11 deg) == 0.981627183447663975712771389226 (0x1.f697d6938b6c2p-1)
gcem::cos(+11 deg) == 0.981627183447663975712771389226 (0x1.f697d6938b6c2p-1)
std::cos (+12 deg) == 0.978147600733805688832944724709 (0x1.f4cfc327a008p-1)
gcem::cos(+12 deg) == 0.978147600733805577810642262193 (0x1.f4cfc327a007fp-1) bug
std::cos (+13 deg) == 0.974370064785235245885530730447 (0x1.f2e0a214e870fp-1)
gcem::cos(+13 deg) == 0.974370064785235245885530730447 (0x1.f2e0a214e870fp-1)
std::cos (+14 deg) == 0.970295726275996472942608761514 (0x1.f0ca99f79ba25p-1)
gcem::cos(+14 deg) == 0.970295726275996583964911224029 (0x1.f0ca99f79ba26p-1) bug
std::cos (+15 deg) == 0.965925826289068312213714762038 (0x1.ee8dd4748bf15p-1)
gcem::cos(+15 deg) == 0.965925826289068312213714762038 (0x1.ee8dd4748bf15p-1)
std::sin (-15 deg) == -0.258819045102520739476403832668 (-0x1.0907dc193069p-2)
gcem::sin(-15 deg) == -0.258819045102520739476403832668 (-0x1.0907dc193069p-2)
std::sin (-14 deg) == -0.241921895599667730047954705697 (-0x1.ef74bf2e4b91dp-3)
gcem::sin(-14 deg) == -0.241921895599667730047954705697 (-0x1.ef74bf2e4b91dp-3)
std::sin (-13 deg) == -0.224951054343865003426472526371 (-0x1.ccb3236cdc675p-3)
gcem::sin(-13 deg) == -0.224951054343864975670896910742 (-0x1.ccb3236cdc674p-3) bug
std::sin (-12 deg) == -0.207911690817759342575499204031 (-0x1.a9cd9ac4258f6p-3)
gcem::sin(-12 deg) == -0.207911690817759314819923588402 (-0x1.a9cd9ac4258f5p-3) bug
std::sin (-11 deg) == -0.190808995376544804356555573577 (-0x1.86c6ddd76624fp-3)
gcem::sin(-11 deg) == -0.190808995376544804356555573577 (-0x1.86c6ddd76624fp-3)
std::sin (-10 deg) == -0.173648177666930331186634361984 (-0x1.63a1a7e0b7389p-3)
gcem::sin(-10 deg) == -0.173648177666930358942209977613 (-0x1.63a1a7e0b738ap-3) bug
std::sin ( -9 deg) == -0.156434465040230868959625354364 (-0x1.4060b67a85375p-3)
gcem::sin( -9 deg) == -0.156434465040230868959625354364 (-0x1.4060b67a85375p-3)
std::sin ( -8 deg) == -0.139173100960065437847745783984 (-0x1.1d06c968d9e19p-3)
gcem::sin( -8 deg) == -0.139173100960065465603321399612 (-0x1.1d06c968d9e1ap-3) bug
std::sin ( -7 deg) == -0.121869343405147476100403025612 (-0x1.f32d44c4f62d3p-4)
gcem::sin( -7 deg) == -0.121869343405147489978190833426 (-0x1.f32d44c4f62d4p-4) bug
std::sin ( -6 deg) == -0.104528463267653470847307062286 (-0x1.ac2609b3c576cp-4)
gcem::sin( -6 deg) == -0.104528463267653456969519254471 (-0x1.ac2609b3c576bp-4) bug
std::sin ( -5 deg) == -0.087155742747658165869850677154 (-0x1.64fd6b8c28102p-4)
gcem::sin( -5 deg) == -0.087155742747658151992062869340 (-0x1.64fd6b8c28101p-4) bug
std::sin ( -4 deg) == -0.069756473744125302438590097154 (-0x1.1db8f6d6a5128p-4)
gcem::sin( -4 deg) == -0.069756473744125302438590097154 (-0x1.1db8f6d6a5128p-4)
std::sin ( -3 deg) == -0.052335956242943834637593170100 (-0x1.acbc748efc90ep-5)
gcem::sin( -3 deg) == -0.052335956242943834637593170100 (-0x1.acbc748efc90ep-5)
std::sin ( -2 deg) == -0.034899496702500969191884649945 (-0x1.1de58c9f7dc27p-5)
gcem::sin( -2 deg) == -0.034899496702500969191884649945 (-0x1.1de58c9f7dc27p-5)
std::sin ( -1 deg) == -0.017452406437283511653202339176 (-0x1.1df0b2b89dd1ep-6)
gcem::sin( -1 deg) == -0.017452406437283511653202339176 (-0x1.1df0b2b89dd1ep-6)
std::sin ( +0 deg) == 0.000000000000000000000000000000 (0x0p+0)
gcem::sin( +0 deg) == 0.000000000000000000000000000000 (0x0p+0)
std::sin ( +1 deg) == 0.017452406437283511653202339176 (0x1.1df0b2b89dd1ep-6)
gcem::sin( +1 deg) == 0.017452406437283511653202339176 (0x1.1df0b2b89dd1ep-6)
std::sin ( +2 deg) == 0.034899496702500969191884649945 (0x1.1de58c9f7dc27p-5)
gcem::sin( +2 deg) == 0.034899496702500969191884649945 (0x1.1de58c9f7dc27p-5)
std::sin ( +3 deg) == 0.052335956242943834637593170100 (0x1.acbc748efc90ep-5)
gcem::sin( +3 deg) == 0.052335956242943834637593170100 (0x1.acbc748efc90ep-5)
std::sin ( +4 deg) == 0.069756473744125302438590097154 (0x1.1db8f6d6a5128p-4)
gcem::sin( +4 deg) == 0.069756473744125302438590097154 (0x1.1db8f6d6a5128p-4)
std::sin ( +5 deg) == 0.087155742747658165869850677154 (0x1.64fd6b8c28102p-4)
gcem::sin( +5 deg) == 0.087155742747658151992062869340 (0x1.64fd6b8c28101p-4) bug
std::sin ( +6 deg) == 0.104528463267653470847307062286 (0x1.ac2609b3c576cp-4)
gcem::sin( +6 deg) == 0.104528463267653456969519254471 (0x1.ac2609b3c576bp-4) bug
std::sin ( +7 deg) == 0.121869343405147476100403025612 (0x1.f32d44c4f62d3p-4)
gcem::sin( +7 deg) == 0.121869343405147489978190833426 (0x1.f32d44c4f62d4p-4) bug
std::sin ( +8 deg) == 0.139173100960065437847745783984 (0x1.1d06c968d9e19p-3)
gcem::sin( +8 deg) == 0.139173100960065465603321399612 (0x1.1d06c968d9e1ap-3) bug
std::sin ( +9 deg) == 0.156434465040230868959625354364 (0x1.4060b67a85375p-3)
gcem::sin( +9 deg) == 0.156434465040230868959625354364 (0x1.4060b67a85375p-3)
std::sin (+10 deg) == 0.173648177666930331186634361984 (0x1.63a1a7e0b7389p-3)
gcem::sin(+10 deg) == 0.173648177666930358942209977613 (0x1.63a1a7e0b738ap-3) bug
std::sin (+11 deg) == 0.190808995376544804356555573577 (0x1.86c6ddd76624fp-3)
gcem::sin(+11 deg) == 0.190808995376544804356555573577 (0x1.86c6ddd76624fp-3)
std::sin (+12 deg) == 0.207911690817759342575499204031 (0x1.a9cd9ac4258f6p-3)
gcem::sin(+12 deg) == 0.207911690817759314819923588402 (0x1.a9cd9ac4258f5p-3) bug
std::sin (+13 deg) == 0.224951054343865003426472526371 (0x1.ccb3236cdc675p-3)
gcem::sin(+13 deg) == 0.224951054343864975670896910742 (0x1.ccb3236cdc674p-3) bug
std::sin (+14 deg) == 0.241921895599667730047954705697 (0x1.ef74bf2e4b91dp-3)
gcem::sin(+14 deg) == 0.241921895599667730047954705697 (0x1.ef74bf2e4b91dp-3)
std::sin (+15 deg) == 0.258819045102520739476403832668 (0x1.0907dc193069p-2)
gcem::sin(+15 deg) == 0.258819045102520739476403832668 (0x1.0907dc193069p-2)
Compile on Windows:
cl /std:c++latest gcem_trig_test.cpp
Output on Windows:
std::cos (-15 deg) == 0.965925826289068312213714762038 (0x1.ee8dd4748bf15p-1)
gcem::cos(-15 deg) == 0.965925826289068312213714762038 (0x1.ee8dd4748bf15p-1)
std::cos (-14 deg) == 0.970295726275996472942608761514 (0x1.f0ca99f79ba25p-1)
gcem::cos(-14 deg) == 0.970295726275996583964911224029 (0x1.f0ca99f79ba26p-1) bug
std::cos (-13 deg) == 0.974370064785235245885530730447 (0x1.f2e0a214e870fp-1)
gcem::cos(-13 deg) == 0.974370064785235245885530730447 (0x1.f2e0a214e870fp-1)
std::cos (-12 deg) == 0.978147600733805688832944724709 (0x1.f4cfc327a0080p-1)
gcem::cos(-12 deg) == 0.978147600733805577810642262193 (0x1.f4cfc327a007fp-1) bug
std::cos (-11 deg) == 0.981627183447663975712771389226 (0x1.f697d6938b6c2p-1)
gcem::cos(-11 deg) == 0.981627183447663975712771389226 (0x1.f697d6938b6c2p-1)
std::cos (-10 deg) == 0.984807753012208020315654266597 (0x1.f838b8c811c17p-1)
gcem::cos(-10 deg) == 0.984807753012208131337956729112 (0x1.f838b8c811c18p-1) bug
std::cos ( -9 deg) == 0.987688340595137770350220307591 (0x1.f9b24942fe45cp-1)
gcem::cos( -9 deg) == 0.987688340595137659327917845076 (0x1.f9b24942fe45bp-1) bug
std::cos ( -8 deg) == 0.990268068741570361979142944620 (0x1.fb046a930947ap-1)
gcem::cos( -8 deg) == 0.990268068741570473001445407135 (0x1.fb046a930947bp-1) bug
std::cos ( -7 deg) == 0.992546151641321983127852490725 (0x1.fc2f025a23e8bp-1)
gcem::cos( -7 deg) == 0.992546151641322094150154953240 (0x1.fc2f025a23e8cp-1) bug
std::cos ( -6 deg) == 0.994521895368273289861349439889 (0x1.fd31f94f867c6p-1)
gcem::cos( -6 deg) == 0.994521895368273289861349439889 (0x1.fd31f94f867c6p-1)
std::cos ( -5 deg) == 0.996194698091745545198705258372 (0x1.fe0d3b41815a2p-1)
gcem::cos( -5 deg) == 0.996194698091745434176402795856 (0x1.fe0d3b41815a1p-1) bug
std::cos ( -4 deg) == 0.997564050259824197652847033169 (0x1.fec0b7170fff6p-1)
gcem::cos( -4 deg) == 0.997564050259824197652847033169 (0x1.fec0b7170fff6p-1)
std::cos ( -3 deg) == 0.998629534754573833232882407174 (0x1.ff4c5ed12e61dp-1)
gcem::cos( -3 deg) == 0.998629534754573833232882407174 (0x1.ff4c5ed12e61dp-1)
std::cos ( -2 deg) == 0.999390827019095762118183756684 (0x1.ffb0278bf0567p-1)
gcem::cos( -2 deg) == 0.999390827019095651095881294168 (0x1.ffb0278bf0566p-1) bug
std::cos ( -1 deg) == 0.999847695156391269577511593525 (0x1.ffec097f5af8ap-1)
gcem::cos( -1 deg) == 0.999847695156391158555209131009 (0x1.ffec097f5af89p-1) bug
std::cos ( +0 deg) == 1.000000000000000000000000000000 (0x1.0000000000000p+0)
gcem::cos( +0 deg) == 1.000000000000000000000000000000 (0x1.0000000000000p+0)
std::cos ( +1 deg) == 0.999847695156391269577511593525 (0x1.ffec097f5af8ap-1)
gcem::cos( +1 deg) == 0.999847695156391158555209131009 (0x1.ffec097f5af89p-1) bug
std::cos ( +2 deg) == 0.999390827019095762118183756684 (0x1.ffb0278bf0567p-1)
gcem::cos( +2 deg) == 0.999390827019095651095881294168 (0x1.ffb0278bf0566p-1) bug
std::cos ( +3 deg) == 0.998629534754573833232882407174 (0x1.ff4c5ed12e61dp-1)
gcem::cos( +3 deg) == 0.998629534754573833232882407174 (0x1.ff4c5ed12e61dp-1)
std::cos ( +4 deg) == 0.997564050259824197652847033169 (0x1.fec0b7170fff6p-1)
gcem::cos( +4 deg) == 0.997564050259824197652847033169 (0x1.fec0b7170fff6p-1)
std::cos ( +5 deg) == 0.996194698091745545198705258372 (0x1.fe0d3b41815a2p-1)
gcem::cos( +5 deg) == 0.996194698091745434176402795856 (0x1.fe0d3b41815a1p-1) bug
std::cos ( +6 deg) == 0.994521895368273289861349439889 (0x1.fd31f94f867c6p-1)
gcem::cos( +6 deg) == 0.994521895368273289861349439889 (0x1.fd31f94f867c6p-1)
std::cos ( +7 deg) == 0.992546151641321983127852490725 (0x1.fc2f025a23e8bp-1)
gcem::cos( +7 deg) == 0.992546151641322094150154953240 (0x1.fc2f025a23e8cp-1) bug
std::cos ( +8 deg) == 0.990268068741570361979142944620 (0x1.fb046a930947ap-1)
gcem::cos( +8 deg) == 0.990268068741570473001445407135 (0x1.fb046a930947bp-1) bug
std::cos ( +9 deg) == 0.987688340595137770350220307591 (0x1.f9b24942fe45cp-1)
gcem::cos( +9 deg) == 0.987688340595137659327917845076 (0x1.f9b24942fe45bp-1) bug
std::cos (+10 deg) == 0.984807753012208020315654266597 (0x1.f838b8c811c17p-1)
gcem::cos(+10 deg) == 0.984807753012208131337956729112 (0x1.f838b8c811c18p-1) bug
std::cos (+11 deg) == 0.981627183447663975712771389226 (0x1.f697d6938b6c2p-1)
gcem::cos(+11 deg) == 0.981627183447663975712771389226 (0x1.f697d6938b6c2p-1)
std::cos (+12 deg) == 0.978147600733805688832944724709 (0x1.f4cfc327a0080p-1)
gcem::cos(+12 deg) == 0.978147600733805577810642262193 (0x1.f4cfc327a007fp-1) bug
std::cos (+13 deg) == 0.974370064785235245885530730447 (0x1.f2e0a214e870fp-1)
gcem::cos(+13 deg) == 0.974370064785235245885530730447 (0x1.f2e0a214e870fp-1)
std::cos (+14 deg) == 0.970295726275996472942608761514 (0x1.f0ca99f79ba25p-1)
gcem::cos(+14 deg) == 0.970295726275996583964911224029 (0x1.f0ca99f79ba26p-1) bug
std::cos (+15 deg) == 0.965925826289068312213714762038 (0x1.ee8dd4748bf15p-1)
gcem::cos(+15 deg) == 0.965925826289068312213714762038 (0x1.ee8dd4748bf15p-1)
std::sin (-15 deg) == -0.258819045102520739476403832668 (-0x1.0907dc1930690p-2)
gcem::sin(-15 deg) == -0.258819045102520739476403832668 (-0x1.0907dc1930690p-2)
std::sin (-14 deg) == -0.241921895599667730047954705697 (-0x1.ef74bf2e4b91dp-3)
gcem::sin(-14 deg) == -0.241921895599667730047954705697 (-0x1.ef74bf2e4b91dp-3)
std::sin (-13 deg) == -0.224951054343865003426472526371 (-0x1.ccb3236cdc675p-3)
gcem::sin(-13 deg) == -0.224951054343864975670896910742 (-0x1.ccb3236cdc674p-3) bug
std::sin (-12 deg) == -0.207911690817759342575499204031 (-0x1.a9cd9ac4258f6p-3)
gcem::sin(-12 deg) == -0.207911690817759314819923588402 (-0x1.a9cd9ac4258f5p-3) bug
std::sin (-11 deg) == -0.190808995376544804356555573577 (-0x1.86c6ddd76624fp-3)
gcem::sin(-11 deg) == -0.190808995376544804356555573577 (-0x1.86c6ddd76624fp-3)
std::sin (-10 deg) == -0.173648177666930331186634361984 (-0x1.63a1a7e0b7389p-3)
gcem::sin(-10 deg) == -0.173648177666930358942209977613 (-0x1.63a1a7e0b738ap-3) bug
std::sin ( -9 deg) == -0.156434465040230868959625354364 (-0x1.4060b67a85375p-3)
gcem::sin( -9 deg) == -0.156434465040230868959625354364 (-0x1.4060b67a85375p-3)
std::sin ( -8 deg) == -0.139173100960065437847745783984 (-0x1.1d06c968d9e19p-3)
gcem::sin( -8 deg) == -0.139173100960065465603321399612 (-0x1.1d06c968d9e1ap-3) bug
std::sin ( -7 deg) == -0.121869343405147476100403025612 (-0x1.f32d44c4f62d3p-4)
gcem::sin( -7 deg) == -0.121869343405147489978190833426 (-0x1.f32d44c4f62d4p-4) bug
std::sin ( -6 deg) == -0.104528463267653470847307062286 (-0x1.ac2609b3c576cp-4)
gcem::sin( -6 deg) == -0.104528463267653456969519254471 (-0x1.ac2609b3c576bp-4) bug
std::sin ( -5 deg) == -0.087155742747658165869850677154 (-0x1.64fd6b8c28102p-4)
gcem::sin( -5 deg) == -0.087155742747658151992062869340 (-0x1.64fd6b8c28101p-4) bug
std::sin ( -4 deg) == -0.069756473744125302438590097154 (-0x1.1db8f6d6a5128p-4)
gcem::sin( -4 deg) == -0.069756473744125302438590097154 (-0x1.1db8f6d6a5128p-4)
std::sin ( -3 deg) == -0.052335956242943834637593170100 (-0x1.acbc748efc90ep-5)
gcem::sin( -3 deg) == -0.052335956242943834637593170100 (-0x1.acbc748efc90ep-5)
std::sin ( -2 deg) == -0.034899496702500969191884649945 (-0x1.1de58c9f7dc27p-5)
gcem::sin( -2 deg) == -0.034899496702500969191884649945 (-0x1.1de58c9f7dc27p-5)
std::sin ( -1 deg) == -0.017452406437283511653202339176 (-0x1.1df0b2b89dd1ep-6)
gcem::sin( -1 deg) == -0.017452406437283511653202339176 (-0x1.1df0b2b89dd1ep-6)
std::sin ( +0 deg) == 0.000000000000000000000000000000 (0x0.0000000000000p+0)
gcem::sin( +0 deg) == 0.000000000000000000000000000000 (0x0.0000000000000p+0)
std::sin ( +1 deg) == 0.017452406437283511653202339176 (0x1.1df0b2b89dd1ep-6)
gcem::sin( +1 deg) == 0.017452406437283511653202339176 (0x1.1df0b2b89dd1ep-6)
std::sin ( +2 deg) == 0.034899496702500969191884649945 (0x1.1de58c9f7dc27p-5)
gcem::sin( +2 deg) == 0.034899496702500969191884649945 (0x1.1de58c9f7dc27p-5)
std::sin ( +3 deg) == 0.052335956242943834637593170100 (0x1.acbc748efc90ep-5)
gcem::sin( +3 deg) == 0.052335956242943834637593170100 (0x1.acbc748efc90ep-5)
std::sin ( +4 deg) == 0.069756473744125302438590097154 (0x1.1db8f6d6a5128p-4)
gcem::sin( +4 deg) == 0.069756473744125302438590097154 (0x1.1db8f6d6a5128p-4)
std::sin ( +5 deg) == 0.087155742747658165869850677154 (0x1.64fd6b8c28102p-4)
gcem::sin( +5 deg) == 0.087155742747658151992062869340 (0x1.64fd6b8c28101p-4) bug
std::sin ( +6 deg) == 0.104528463267653470847307062286 (0x1.ac2609b3c576cp-4)
gcem::sin( +6 deg) == 0.104528463267653456969519254471 (0x1.ac2609b3c576bp-4) bug
std::sin ( +7 deg) == 0.121869343405147476100403025612 (0x1.f32d44c4f62d3p-4)
gcem::sin( +7 deg) == 0.121869343405147489978190833426 (0x1.f32d44c4f62d4p-4) bug
std::sin ( +8 deg) == 0.139173100960065437847745783984 (0x1.1d06c968d9e19p-3)
gcem::sin( +8 deg) == 0.139173100960065465603321399612 (0x1.1d06c968d9e1ap-3) bug
std::sin ( +9 deg) == 0.156434465040230868959625354364 (0x1.4060b67a85375p-3)
gcem::sin( +9 deg) == 0.156434465040230868959625354364 (0x1.4060b67a85375p-3)
std::sin (+10 deg) == 0.173648177666930331186634361984 (0x1.63a1a7e0b7389p-3)
gcem::sin(+10 deg) == 0.173648177666930358942209977613 (0x1.63a1a7e0b738ap-3) bug
std::sin (+11 deg) == 0.190808995376544804356555573577 (0x1.86c6ddd76624fp-3)
gcem::sin(+11 deg) == 0.190808995376544804356555573577 (0x1.86c6ddd76624fp-3)
std::sin (+12 deg) == 0.207911690817759342575499204031 (0x1.a9cd9ac4258f6p-3)
gcem::sin(+12 deg) == 0.207911690817759314819923588402 (0x1.a9cd9ac4258f5p-3) bug
std::sin (+13 deg) == 0.224951054343865003426472526371 (0x1.ccb3236cdc675p-3)
gcem::sin(+13 deg) == 0.224951054343864975670896910742 (0x1.ccb3236cdc674p-3) bug
std::sin (+14 deg) == 0.241921895599667730047954705697 (0x1.ef74bf2e4b91dp-3)
gcem::sin(+14 deg) == 0.241921895599667730047954705697 (0x1.ef74bf2e4b91dp-3)
std::sin (+15 deg) == 0.258819045102520739476403832668 (0x1.0907dc1930690p-2)
gcem::sin(+15 deg) == 0.258819045102520739476403832668 (0x1.0907dc1930690p-2)
An inverse square root is a rather popular function. Would be nice to have it. Wikipedia suggests a constexpr implementation. Further sections also reference research on better magic number, e.g. 10.1109/JPROC.2020.2991885.
using
constexpr float result = gcem::pow(5,0.5);
fails with the following error:
error: function call must have a constant value in a constant expression
.../external/gcem/gcem_incl/log.hpp(106): note: floating-point values cannot be compared
.../external/gcem/gcem_incl/log.hpp(144): note: called from:
.../external/gcem/gcem_incl/pow.hpp(37): note: called from:
.../external/gcem/gcem_incl/pow.hpp(50): note: called from:
.../external/gcem/gcem_incl/pow.hpp(79): note: called from:
I'm using the nvidia nvcc compiler version 9.1 with gcc 6.5.
Using pow with integers works fine.
C++20 comes with a handy is_constant_evaluated
which can be used to detect evaluation context.
Essentially, it can allow gcem to be a complete wrapper around stdlib operations (where stdlib is being replaced for compile-time context) and better runtime performance.
Moreover, without depending on C++ version, __cpp_lib_is_constant_evaluated
can be used to check if the compiler provides the function (from header <type_traits>
)
Before:
template<typename T>
constexpr
return_t<T>
ceil(const T x)
noexcept
{
return internal::ceil_check( static_cast<return_t<T>>(x) );
}
After:
template<typename T>
constexpr
return_t<T>
ceil(const T x)
noexcept
{
#ifdef __cpp_lib_is_constant_evaluated
if (!std::is_constant_evaluated()) return std::ceil(static_cast<return_t<T>>(x));
#endif
return internal::ceil_check( static_cast<return_t<T>>(x) );
}
Benefits:
Cons: Slight more code
In one of my project that I am working on, I found out that fmod
is not available (or not with the same name). Consider adding one?
Hi, this is a small thing.
I've inherited inclusion of some macros from <math.h> from some other libraries that I use in a project. The compiler tries to expand in your awesome gcem library, braking the code. An easy fix is to add
#undef abs
#undef round
#undef signbit
just before inclusion of . Looking into , they've done something similar (at least for ARM GCC):
// Get rid of those macros defined in <math.h> in lieu of real functions.
#undef abs
#undef div
#undef acos
#undef asin
#undef atan
#undef atan2
#undef ceil
#undef cos
#undef cosh
#undef exp
#undef fabs
#undef floor
#undef fmod
#undef frexp
#undef ldexp
#undef log
#undef log10
#undef modf
#undef pow
#undef sin
#undef sinh
#undef sqrt
#undef tan
#undef tanh
Maybe this would be an idea for your lib? It'l take you all of 5 seconds to make the change and ensure compatibility with older code bases. Just a thought.
Thanks and have a nice day.
Niels
The following functions are not available:
Is there a plan to implement them in the future?
there's might** be overflow issues
e.g.
abs function returns x == T(0) ? T(0) : x < T(0) ? - x : x
since abs(min limit) == max_limit + 1
the result for abs(INT64_MIN) is -9223372036854775808 (negative)
INT64_MIN == 0 ? 0 : INT64_MIN < 0 ? -INT64_MIN : INT64_MIN
** = i didn't executed the code since i'm using it as reference
I have some functions in my library that need to be called at both compile-time and runtime, and cmath has varying degrees of support for constexpr on different platforms, so I chose to use gcem.
But in using it, I found that many of gcem's functions are an order of magnitude slower than cmath under O3 optimization. I know that I can write two versions that are called at compile time and at runtime, but I'm wondering why gcem is so much slower at runtime?
I've tested this under x86 linux, windows and mac, compiling with g++, msvc and apple clang respectively, and all get roughly the same results.
Hi @kthohr
Thanks for this package!!
I would like to rely on your wonderful package. Therefore I would also need a conda-forge feedstock. Would you be interested to become a co-maintainer ?
If so please comment "I agree to be a maintainer" in the PR : conda-forge/staged-recipes#14818 .
(If you see this after the PR is merged I will add you to the feedstock!)
atan2()
is simple to implement in terms of atan()
and would be handy to have.
It would be great to have the full abs
function set for compatibility purpose with standard library.
https://en.cppreference.com/w/cpp/numeric/math/fabs
gcem::sqrt(DBL_MAX) fails with msvc or clang for the following reason :
failure was caused by evaluation exceeding call depth limit of 512 (/constexpr:depth)
not very important (I can replace the value) but you may have wanted to know
Workarounding by log(x)/log(10)
First of all, thank you so much for this library! I've been using it to build a compile-time filter design library [1] and it's been working (almost) flawlessly! :)
When using the pow()
function with a small base argument to compute n-th roots, I found that it would always return zero.
#include <cmath>
#include <iomanip>
#include <iostream>
#include <gcem.hpp>
int main()
{
std::cout << std::setprecision(20);
std::cout << "std::pow(): " << std::pow(1e-20, 1.0/8.0) << "\n";
std::cout << "gcem::pow(): " << gcem::pow(1e-20, 1.0/8.0) << "\n";
}
This will output:
std::pow(): 0.0031622776601683793944
gcem::pow(): 0
So I dug a little deeper and the reason is that log()
, which is used to implement pow()
, has this check:
// x ~= 0
GCLIM<T>::epsilon() > x ? \
- GCLIM<T>::infinity() :
Now ::epsilon()
is typically much larger than the smallest representable floating point number and as a result, log()
will always return infinity for small input arguments. The same check is present in log2()
and log10()
.
Replacing this check with a simple check for non-zeroness makes the code work as expected.
[1] Part of libembedded.
It would be very handy to have constexpr clamp function here. It isn't avalible at c++11 at all and having math library that would take care of it would be great.
When input is 1.0 to a negative even float type number, gcem::log10 return a wrong negative odd number.
Eg: gcem::log10(0.01) = -1.0f, should be -2.0f
Tested with 1e-2f to 1e-26f, only the 1e-22f is right.
In double and long double type, this also happens to 1e-12 and 1e-14.
Envrioment: MSVC 14.41.33901, C++20 std
GCE-Math version 1.18.0
I made some preliminary performance benchmarks on Cortex-M3.
Besides the performance gain, the memory requirements are lower and most important deterministic.
This is especially crucial for embedded systems.
arm-none-eabi-gcc (Fedora 13.2.0-5.fc40) 13.2.0
-mcpu=cortex-m3 -mthumb -O1 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -ffreestanding
-mcpu=cortex-m3 -mthumb -Os -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -ffreestanding
sqrt
implemented in loop
-O1
sqrt(0.6) = 0.77460: 12448
floor(0.6) = 0.00000: 1272
fabs(0.6) = 0.60000: 212
log(0.6) = -0.51083: 29780
-Os
sqrt(0.6) = 0.77460: 11825
floor(0.6) = 0.00000: 1262
fabs(0.6) = 0.60000: 241
log(0.6) = -0.51083: 29925
sqrt
implemented recursively
-O1
sqrt(0.6) = 0.77460: 16359
floor(0.6) = 0.00000: 1265
fabs(0.6) = 0.60000: 207
log(0.6) = -0.51083: 30062
-Os
sqrt(0.6) = 0.77460: 15055
floor(0.6) = 0.00000: 1332
fabs(0.6) = 0.60000: 240
log(0.6) = -0.51083: 29429
I tried incomplete_gamma_inv
to help me convert a flat random distribution to a gamma-distribution. When I try a large sample most inputs work well, but I get NaNs with some specific values as listed below:
#include <gcem.hpp>
#include <iostream>
#include <vector>
int main()
{
std::vector<double> r = {2.26397533e-05, 9.99999672e-01, 7.41391908e-04, 3.88840912e-04,
7.33291963e-04, 2.62747984e-04, 2.54816143e-04, 1.69432024e-04,
1.75788999e-04, 2.59617111e-04, 4.94140433e-04, 6.44463347e-04};
for (auto& i : r) {
std::cout << gcem::incomplete_gamma_inv(2.0, i) << std::endl;
}
return 0;
}
which gives:
nan
nan
nan
nan
nan
nan
nan
nan
nan
nan
nan
nan
Note that most are quite small, but not even all of them. Do you know what is going on here?
Visual Studio 2019 16.9.6 x86.
#include "c:/dev/repos/github.com/kthohr/gcem/include/gcem.hpp"
int main()
{
static constexpr float const s_number = 0.5f;
static constexpr float const s_computation = gcem::exp(s_number);
}
Build started...
1>------ Build started: Project: Project2, Configuration: Debug Win32 ------
1>Source.cpp
1>c:\dev\repos\github.com\kthohr\gcem\include\gcem_incl\exp.hpp(61,68): warning C4244: 'return': conversion from 'T1' to 'T', possible loss of data
1> with
1> [
1> T1=long double
1> ]
1> and
1> [
1> T=float
1> ]
1>c:\dev\repos\github.com\kthohr\gcem\include\gcem_incl\exp.hpp(82): message : see reference to function template instantiation 'T gcem::internal::exp_split<T>(const T) noexcept' being compiled
1> with
1> [
1> T=float
1> ]
1>c:\dev\repos\github.com\kthohr\gcem\include\gcem_incl\exp.hpp(103): message : see reference to function template instantiation 'T gcem::internal::exp_check<float>(const T) noexcept' being compiled
1> with
1> [
1> T=float
1> ]
1>C:\Users\knapekma\source\repos\Project2\Project2\Source.cpp(13): message : see reference to function template instantiation 'float gcem::exp<float>(const T) noexcept' being compiled
1> with
1> [
1> T=float
1> ]
1>Done building project "Project2.vcxproj".
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
The following program compares gcem::sin
with GCC's __builtin_sin
for input of the form 2**n
with n
from 0 to 63. It appears that already for input of 512.0
relative error is more than 5e-13
, and for larger input relative error tends to become larger. At input of 2**52
relative error reaches 0.3
, and starting from 2**55
the result is simply zero.
I do admit that range reduction is hard to do, but I suggest to at least warn the users (in the README?) that the library is not intended to handle most of the range of the data types it works with.
BTW, why not implement the functions in terms of long double
internally (at least when T
is a builtin floating-point type)? It would already improve precision, and since it's constexpr
, performance is not a problem.
#include <array>
#include <limits>
#include <iomanip>
#include <gcem.hpp>
#include <iostream>
template<unsigned N>
constexpr std::array<double, N> sinPow2_gcem()
{
static_assert(N<=64);
std::array<double, N> arr{};
for(unsigned n=0;n<N;++n)
{
arr[n]=gcem::sin(static_cast<double>(1ull<<n));
}
return arr;
}
template<unsigned N>
constexpr std::array<double, N> sinPow2_gcc()
{
static_assert(N<=64);
std::array<double, N> arr{};
for(unsigned n=0;n<N;++n)
{
arr[n]=__builtin_sin(static_cast<double>(1ull<<n));
}
return arr;
}
int main()
{
constexpr auto table_gcem=sinPow2_gcem<64>();
constexpr auto table_gcc =sinPow2_gcc <64>();
for(unsigned n=0;n<table_gcem.size();++n)
{
std::cout << std::defaultfloat << "sin(2^" << std::setw(2) << n << ") = "
<< std::setprecision(std::numeric_limits<double>::max_digits10)
<< std::setw(21) << table_gcem[n] << " or "
<< std::setw(21) << table_gcc[n] << ", rel_diff: "
<< std::scientific << std::setprecision(2)
<< std::abs((table_gcem[n]-table_gcc[n])/table_gcc[n]) << "\n";
}
}
Output on my system with GCC version 8.3.0-6ubuntu1~18.04:
sin(2^ 0) = 0.8414709848078965 or 0.8414709848078965, rel_diff: 0.00e+00
sin(2^ 1) = 0.90929742682568182 or 0.90929742682568171, rel_diff: 1.22e-16
sin(2^ 2) = -0.7568024953079282 or -0.7568024953079282, rel_diff: 0.00e+00
sin(2^ 3) = 0.98935824662338168 or 0.98935824662338179, rel_diff: 1.12e-16
sin(2^ 4) = -0.28790331666506591 or -0.2879033166650653, rel_diff: 2.12e-15
sin(2^ 5) = 0.55142668124169159 or 0.55142668124169059, rel_diff: 1.81e-15
sin(2^ 6) = 0.92002603819679174 or 0.92002603819679063, rel_diff: 1.21e-15
sin(2^ 7) = 0.72103771050172805 or 0.7210377105017316, rel_diff: 4.93e-15
sin(2^ 8) = -0.99920803410706305 or -0.99920803410706271, rel_diff: 3.33e-16
sin(2^ 9) = 0.079518494012835325 or 0.079518494012876348, rel_diff: 5.16e-13
sin(2^10) = -0.15853338004391457 or -0.15853338004399595, rel_diff: 5.13e-13
sin(2^11) = -0.31305701278994652 or -0.31305701279012343, rel_diff: 5.65e-13
sin(2^12) = -0.59464198760808085 or -0.59464198760821463, rel_diff: 2.25e-13
sin(2^13) = -0.95617315284297588 or -0.9561731528431463, rel_diff: 1.78e-13
sin(2^14) = -0.55993846566976402 or -0.55993846566934702, rel_diff: 7.45e-13
sin(2^15) = 0.92785633341405394 or 0.92785633341392471, rel_diff: 1.39e-13
sin(2^16) = 0.69206545382222318 or 0.69206545382272322, rel_diff: 7.23e-13
sin(2^17) = -0.99911378895071246 or -0.99911378895077085, rel_diff: 5.84e-14
sin(2^18) = -0.084107027822479849 or -0.084107027809500717, rel_diff: 1.54e-10
sin(2^19) = 0.16761802725644473 or 0.1676180272206543, rel_diff: 2.14e-10
sin(2^20) = 0.33049314009026282 or 0.33049314002173469, rel_diff: 2.07e-10
sin(2^21) = 0.62384439947212311 or 0.62384439935862968, rel_diff: 1.82e-10
sin(2^22) = 0.97512939500607743 or 0.97512939494170703, rel_diff: 6.60e-11
sin(2^23) = 0.43224820173300005 or 0.43224820225679778, rel_diff: 1.21e-09
sin(2^24) = -0.77956367249015968 or -0.77956367321777775, rel_diff: 9.33e-10
sin(2^25) = -0.97651729150386235 or -0.97651729095092843, rel_diff: 5.66e-10
sin(2^26) = 0.42075989626084753 or 0.42075989775848105, rel_diff: 3.56e-09
sin(2^27) = -0.76340322666929872 or -0.76340322880198075, rel_diff: 2.79e-09
sin(2^28) = -0.98619821358735438 or -0.98619821183697565, rel_diff: 1.77e-09
sin(2^29) = 0.32656763928319343 or 0.32656766301856333, rel_diff: 7.27e-08
sin(2^30) = -0.61732637553418868 or -0.61732641504604213, rel_diff: 6.40e-08
sin(2^31) = -0.97131013678590572 or -0.97131017579293921, rel_diff: 4.02e-08
sin(2^32) = -0.46198671538301844 or -0.46198657951383493, rel_diff: 2.94e-07
sin(2^33) = 0.81946005342466022 or 0.8194597047356359, rel_diff: 4.26e-07
sin(2^34) = 0.93932460955758246 or 0.93932502694657105, rel_diff: 4.44e-07
sin(2^35) = -0.64443221186658051 or -0.64443035102329116, rel_diff: 2.89e-06
sin(2^36) = 0.98554449814305511 or 0.9855441071151041, rel_diff: 3.97e-07
sin(2^37) = 0.33393553242896457 or 0.33393988357522025, rel_diff: 1.30e-05
sin(2^38) = -0.62953253790928321 or -0.62953971111701379, rel_diff: 1.14e-05
sin(2^39) = -0.97826467462457967 or -0.97826480872807564, rel_diff: 1.37e-07
sin(2^40) = -0.40568990812427469 or -0.40570501153282873, rel_diff: 3.72e-05
sin(2^41) = 0.74176170084066606 or 0.74163206513677404, rel_diff: 1.75e-04
sin(2^42) = 0.99494505102476649 or 0.99498379417508209, rel_diff: 3.89e-05
sin(2^43) = -0.19982643887478732 or -0.19906887541578028, rel_diff: 3.81e-03
sin(2^44) = 0.39157600746803073 or 0.39016922335187676, rel_diff: 3.61e-03
sin(2^45) = 0.72061401054487106 or 0.71849129172091508, rel_diff: 2.95e-03
sin(2^46) = 0.99925593527723167 or 0.99947305248379947, rel_diff: 2.17e-04
sin(2^47) = -0.077080812954327838 or -0.064884736238273691, rel_diff: 1.88e-01
sin(2^48) = 0.15561499277355606 or 0.12949601773888192, rel_diff: 2.02e-01
sin(2^49) = 0.30743851458038091 or 0.256811307519207, rel_diff: 1.97e-01
sin(2^50) = 0.58509727294046221 or 0.49639651520894085, rel_diff: 1.79e-01
sin(2^51) = 0.94898461935558631 or 0.86183956388130056, rel_diff: 1.01e-01
sin(2^52) = 0.59847214410395666 or 0.87421730262363506, rel_diff: 3.15e-01
sin(2^53) = -0.95892427466313834 or -0.84892596481465499, rel_diff: 1.30e-01
sin(2^54) = 0.90929742682568182 or 0.89733475299759258, rel_diff: 1.33e-02
sin(2^55) = 0 or -0.79207844079082801, rel_diff: 1.00e+00
sin(2^56) = 0 or 0.96699996306127067, rel_diff: 1.00e+00
sin(2^57) = 0 or -0.49273775680001242, rel_diff: 1.00e+00
sin(2^58) = 0 or 0.85753897066968443, rel_diff: 1.00e+00
sin(2^59) = 0 or 0.88226868987759099, rel_diff: 1.00e+00
sin(2^60) = 0 or -0.83064921763725463, rel_diff: 1.00e+00
sin(2^61) = 0 or 0.92500446025316185, rel_diff: 1.00e+00
sin(2^62) = 0 or -0.70292244361920886, rel_diff: 1.00e+00
sin(2^63) = 0 or 0.9999303766734422, rel_diff: 1.00e+00
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.