c++floating-pointprecisionmodulonumerical-computing

Wrapping around very large angles to [0,2*PI] adds too big numerical error


In below sample, I try different methods to wrap an angle between 0 and 2 x PI. While doing this to compute cosine of same angles, both methods cause a different amount of numerical error compared to the standard std::cos(original angle that is non-wrapped around):

#include <iostream>
#include <cmath>

float fmodfast(float angle)
{
    constexpr float twoPi = 2.0 * 3.141592865358979;
    constexpr float twoPiInv = 1.0/twoPi;
    auto r =  angle - twoPi * floor( angle *twoPiInv) ;
    if(r<0)
    return r+2.0 * 3.141592865358979;
    else
    return r;
}

float fmodn(float f)
{
    auto r = std::fmod(f,2.0 * 3.141592865358979);
    if(r<0)
    return r + 2.0 * 3.141592865358979;
    else
    return r;
}

int main()
{
    for(int i=-100;i<=100;i++)
    {
        float f = 100000000.0f*i;
        std::cout<<f<<" "<<std::cos(f)<<" "<<std::cos(fmodn(f))<<" "<<std::cos(fmodfast(f))<<std::endl;
    }
    return 0;
}

output:

-1e+10 0.87312 -0.659246 0.566338
-9.9e+09 -0.605079 0.624035 0.907523
-9.8e+09 0.386995 -0.451729 0.966069
-9.7e+09 0.0143294 0.4103 0.699183
-9.6e+09 -0.413259 -0.368006 0.236586
-9.5e+09 0.743126 0.324936 -0.292121
-9.4e+09 -0.948805 -0.281181 -0.739178
-9.3e+09 0.997629 0.0797464 -0.999019
-9.2e+09 -0.941808 -0.033905 -0.881843
-9.1e+09 0.728594 -0.0120074 -0.517957
-9e+09 -0.534379 0.215498 0.149652
-8.9e+09 0.151487 -0.260095 0.633131
-8.8e+09 0.256721 0.304145 0.939413
-8.7e+09 -0.622026 -0.347552 0.982845
-8.6e+09 0.883381 0.390228 0.751266
-8.5e+09 -0.972451 -0.569644 0.15466
-8.4e+09 0.984477 0.606771 -0.370933
-8.3e+09 -0.831981 -0.642619 -0.792816
-8.2e+09 -0.605642 -0.733507 0.999185
-8.1e+09 -0.313118 -0.812848 -0.839406
-8e+09 -0.0930906 0.838729 -0.444749
-7.9e+09 0.483742 -0.862841 0.0744413
-7.8e+09 0.742653 -0.919348 -0.636155
-7.7e+09 0.920407 -0.961381 0.964748
-7.6e+09 -0.99995 0.973001 0.964021
-7.5e+09 0.912384 -0.98257 0.693554
-7.4e+09 0.729078 -0.998114 -0.150839
-7.3e+09 0.466099 -0.997944 -0.44715
-7.2e+09 -0.0731113 0.99395 -0.840921
-7.1e+09 -0.332095 -0.987861 -0.999333
-7e+09 -0.621473 -0.960632 0.837297
-6.9e+09 -0.842937 -0.918279 -0.368436
-6.8e+09 0.987798 0.899137 0.15742
-6.7e+09 -0.967582 -0.878099 0.639134
-6.6e+09 -0.832373 -0.811268 -0.965777
-6.5e+09 -0.606204 -0.731665 0.938477
-6.4e+09 0.237293 0.699603 0.63099
-6.3e+09 0.171273 -0.666066 0.146945
-6.2e+09 0.483124 -0.567417 0.450656
-6.1e+09 0.742179 -0.459834 -0.88313
-6e+09 -0.948357 0.418588 -0.998893
-5.9e+09 0.996049 -0.376458 -0.835147
-5.8e+09 0.912673 -0.25748 0.364789
-5.7e+09 0.729562 -0.134448 0.239247
-5.6e+09 -0.39492 0.0888206 0.7011
-5.5e+09 -0.00571906 -0.0430046 0.966773
-5.4e+09 -0.331428 0.0824449 -0.937129
-5.3e+09 -0.620919 0.206597 0.564032
-5.2e+09 0.882717 -0.251292 0.0638678
-5.1e+09 -0.997 0.295457 -0.454168
-5e+09 -0.96776 0.412768 0.884964
-4.9e+09 -0.832765 0.52358 -0.991482
-4.8e+09 0.541637 -0.562136 -0.786322
-4.7e+09 -0.159993 0.599508 -0.361133
-4.6e+09 0.170576 0.695016 -0.243052
-4.5e+09 0.482505 0.779582 0.758174
-4.4e+09 -0.792692 -0.80751 0.984737
-4.3e+09 0.970407 0.833735 0.935753
-4.2e+09 0.996112 0.896318 -0.560834
-4.1e+09 -0.444049 -0.365029 0.999796
-4e+09 -0.67339 -0.958835 -0.526902
-3.9e+09 0.933449 0.200814 -0.426532
-3.8e+09 -0.00501223 0.993227 0.990961
-3.7e+09 -0.929806 -0.0307075 -0.651597
-3.6e+09 0.680767 -0.998486 -0.282036
-3.5e+09 0.435044 -0.140299 0.958462
-3.4e+09 -0.996945 0.974458 -0.760724
-3.3e+09 0.289505 0.307191 -0.130821
-3.2e+09 0.786541 -0.921847 0.903051
-3.1e+09 -0.86114 -0.465072 -0.851661
-3e+09 -0.16069 0.842196 0.0235201
-2.9e+09 0.977925 0.609311 0.82607
-2.8e+09 -0.550036 -0.737842 -0.922253
-2.7e+09 -0.578175 -0.735678 0.177301
-2.6e+09 0.970236 0.611845 0.729344
-2.5e+09 -0.126964 0.840466 -0.970815
-2.4e+09 -0.877963 -0.467902 0.326861
-2.3e+09 0.765041 -0.920602 0.6152
-2.2e+09 0.321954 0.310234 -0.996177
-2.1e+09 -0.999027 0.973734 0.468597
-2e+09 0.40411 -0.143466 0.486363
-1.9e+09 0.705332 -0.998305 -0.997738
-1.8e+09 -0.916724 -0.0275091 0.59915
-1.7e+09 -0.0390845 0.993594 0.345888
-1.6e+09 0.94513 0.197678 -0.975461
-1.5e+09 -0.647808 -0.959738 0.715374
-1.4e+09 -0.474323 -0.362048 0.197164
-1.3e+09 0.992531 0.897732 -0.929878
-1.2e+09 -0.24702 0.5158 0.814514
-1.1e+09 -0.813005 -0.809393 0.0437215
-1e+09 0.837887 -0.654421 -0.86208
-9e+08 0.204053 0.697313 0.894191
-8e+08 -0.986187 0.773847 -0.110766
-7e+08 0.512678 -0.564779 -0.773681
-6e+08 0.613588 -0.870574 0.9525
-5e+08 -0.958615 0.41568 -0.262603
-4e+08 0.0831051 0.941766 -0.666796
-3e+08 0.898217 -0.254387 0.988054
-2e+08 -0.735903 -0.985334 -0.408169
-1e+08 -0.363385 0.0856332 -0.543981
0 1 1 1
1e+08 -0.363385 0.0856329 -0.543981
2e+08 -0.735903 -0.985334 -0.408169
3e+08 0.898217 -0.254387 0.988054
4e+08 0.0831051 0.941766 -0.666796
5e+08 -0.958615 0.41568 -0.262603
6e+08 0.613588 -0.870575 0.9525
7e+08 0.512678 -0.564779 -0.773681
8e+08 -0.986187 0.773847 -0.110767
9e+08 0.204053 0.697313 0.89419
1e+09 0.837887 -0.654421 -0.86208
1.1e+09 -0.813005 -0.809393 0.0437209
1.2e+09 -0.24702 0.5158 0.814513
1.3e+09 0.992531 0.897732 -0.929877
1.4e+09 -0.474323 -0.362049 0.197164
1.5e+09 -0.647808 -0.959738 0.715375
1.6e+09 0.94513 0.197679 -0.975461
1.7e+09 -0.0390845 0.993594 0.345886
1.8e+09 -0.916724 -0.0275096 0.599152
1.9e+09 0.705332 -0.998305 -0.997738
2e+09 0.40411 -0.143466 0.486363
2.1e+09 -0.999027 0.973735 0.468597
2.2e+09 0.321954 0.310234 -0.996177
2.3e+09 0.765041 -0.920602 0.615201
2.4e+09 -0.877963 -0.467901 0.326864
2.5e+09 -0.126964 0.840467 -0.970815
2.6e+09 0.970236 0.611845 0.729342
2.7e+09 -0.578175 -0.735678 0.177302
2.8e+09 -0.550036 -0.737841 -0.922253
2.9e+09 0.977925 0.609311 0.826071
3e+09 -0.16069 0.842196 0.0235226
3.1e+09 -0.86114 -0.465072 -0.851663
3.2e+09 0.786541 -0.921847 0.90305
3.3e+09 0.289505 0.307191 -0.130818
3.4e+09 -0.996945 0.974458 -0.760726
3.5e+09 0.435044 -0.1403 0.958461
3.6e+09 0.680767 -0.998486 -0.282033
3.7e+09 -0.929806 -0.0307068 -0.651599
3.8e+09 -0.00501223 0.993227 0.990963
3.9e+09 0.933449 0.200813 -0.42653
4e+09 -0.67339 -0.958835 -0.526902
4.1e+09 -0.444049 -0.365029 0.999796
4.2e+09 0.996112 0.896318 -0.560833
4.3e+09 0.970407 0.833735 0.935752
4.4e+09 -0.792692 -0.80751 0.984737
4.5e+09 0.482505 0.779583 0.758174
4.6e+09 0.170576 0.695016 -0.243055
4.7e+09 -0.159993 0.599508 -0.361131
4.8e+09 0.541637 -0.562136 -0.78632
4.9e+09 -0.832765 0.52358 -0.991482
5e+09 -0.96776 0.412768 0.884965
5.1e+09 -0.997 0.295458 -0.454157
5.2e+09 0.882717 -0.251292 0.0638805
5.3e+09 -0.620919 0.206597 0.564043
5.4e+09 -0.331428 0.0824454 -0.937128
5.5e+09 -0.00571906 -0.0430041 0.966773
5.6e+09 -0.39492 0.0888199 0.701101
5.7e+09 0.729562 -0.134448 0.239247
5.8e+09 0.912673 -0.257479 0.364786
5.9e+09 0.996049 -0.376457 -0.835145
6e+09 -0.948357 0.418587 -0.998893
6.1e+09 0.742179 -0.459834 -0.883132
6.2e+09 0.483124 -0.567416 0.450658
6.3e+09 0.171273 -0.666065 0.146957
6.4e+09 0.237293 0.699602 0.631
6.5e+09 -0.606204 -0.731664 0.938476
6.6e+09 -0.832373 -0.811267 -0.965773
6.7e+09 -0.967582 -0.878098 0.639135
6.8e+09 0.987798 0.899137 0.157407
6.9e+09 -0.842937 -0.918279 -0.368437
7e+09 -0.621473 -0.960632 0.837297
7.1e+09 -0.332095 -0.987861 -0.999333
7.2e+09 -0.0731113 0.99395 -0.840914
7.3e+09 0.466099 -0.997944 -0.447153
7.4e+09 0.729078 -0.998114 -0.150837
7.5e+09 0.912384 -0.98257 0.693552
7.6e+09 -0.99995 0.973001 0.964016
7.7e+09 0.920407 -0.961381 0.964748
7.8e+09 0.742653 -0.919348 -0.636145
7.9e+09 0.483742 -0.862841 0.0744286
8e+09 -0.0930906 0.838729 -0.444747
8.1e+09 -0.313118 -0.812848 -0.839413
8.2e+09 -0.605642 -0.733508 0.999184
8.3e+09 -0.831981 -0.642619 -0.792808
8.4e+09 0.984477 0.606772 -0.370932
8.5e+09 -0.972451 -0.569644 0.154672
8.6e+09 0.883381 0.390229 0.751264
8.7e+09 -0.622026 -0.347553 0.982842
8.8e+09 0.256721 0.304145 0.939414
8.9e+09 0.151487 -0.260096 0.633145
9e+09 -0.534379 0.215498 0.149654
9.1e+09 0.728594 -0.0120078 -0.517955
9.2e+09 -0.941808 -0.0339047 -0.881849
9.3e+09 0.997629 0.079746 -0.99902
9.4e+09 -0.948805 -0.281181 -0.739169
9.5e+09 0.743126 0.324936 -0.292122
9.6e+09 -0.413259 -0.368005 0.236598
9.7e+09 0.0143294 0.4103 0.699183
9.8e+09 0.386995 -0.451728 0.966072
9.9e+09 -0.605079 0.624035 0.907524
1e+10 0.87312 -0.659246 0.566327

The error diminishes when input angle approaches zero. What is the cause of this floating-point error and how does std::cos solve this problem inside the implementation?

If f is not multiplied by 100000000.0f but 1000.0f then both versions compute very close to std::cos(original angle) with the error on the order of only 0.0001.

Is there a way to wrap around the angle to the range of [0,2pi] without losing any accuracy/precision (without skewing the real angle wrap-around between 0-2pi)? Or, does std::cos not use float inside? I assume it is using float in all parts of algorithm but I may be wrong.


Solution

  • There is a typo in the pi constants:

    3.141592865358979
            ^
    

    The correct value is

    3.1415926535897932384626433832795
    

    cppreference like https://en.cppreference.com/w/cpp/numeric/math/cos uses

    const double pi = std::acos(-1);
    

    to get pi.