1

I've written a simple C++ macro for use with the CERN ROOT data analysis framework. It takes in a data file (essentially a spreadsheet) with day, hour, minute, second, and sub-second columns. My goal here is to convert that data into a file of timestamps, in seconds. Everything works fine up to adding the sub seconds. For some reason, it seems to be rounding the result. For example, one timestamp is:

804267 + 0.5606663227081298828125 = 804267.5625

Another is:

155034 + 0.0958281949540391919293 = 155034

ROOT::RDataFrame d("N", "mydata1.root");
TFile *rfout = new TFile("./mydata2.root", "recreate");
TNtuple *N = new TNtuple("N","N","TIMESTAMP");

vector<float> timestamp;
int i;

d.Foreach([&](float day){timestamp.push_back(day*86400.00);},{"day"});
d.Foreach([&](float hr){timestamp.at(i) = timestamp.at(i)+(hr*3600);i++;},{"hr"});
i=0;
d.Foreach([&](float min){timestamp.at(i) = timestamp.at(i)+(min*60);i++;},{"min"});
i=0;
d.Foreach([&](float sec){timestamp.at(i) = timestamp.at(i)+sec;i++;},{"sec"});

i=0;
float j;
d.Foreach([&](float sub){
    while(sub > 1){
        sub = sub/10;
    }
    j = sub + timestamp.at(i);
    N->Fill(j);
    std::cout << std::setprecision(100) << j << " " << sub <<std::endl;
    i++;
},{"subsecond"});

rfout->Write();
rfout->Close();
abort();

}`

INTorFLOAT
  • 21
  • 1
  • 4
    You are aware that floating point has finite precision? And that a typical `float` can only (reliably) represent about eight significant figures (in decimal). Look at the values you are adding up. The first example is adding a value with 6 significant figures to one with a 22 significant figures. The 22 figure one will be rounded when storing it. And, since the two values have different magnitude, the addition will round again. [Things are a little more complicated than that, since floating point does rounding in base 2, not decimal, but the principle is the same]. – Peter Jul 27 '20 at 08:17

1 Answers1

3

For some reason, it seems to be rounding the result.

The reason has named IEEE_754. If you want to minimise loss of summing float jast sum it in order from smallest to largest.

So, in you case:

...
vector<float> timestamp(d.Size(), 0.0f); // Note: I guess, ROOT::RDataFrame has the member function Size()
d.Foreach([i=0,&timestamp]mutable(float sub){timestamp[i]+=sub; i++;},{"subsecond"});
d.Foreach([i=0,&timestamp]mutable(float sec){timestamp[i]+=sec; i++;},{"sec"});
d.Foreach([i=0,&timestamp]mutable(float min){timestamp[i]+=min*60; i++;},{"min"});
d.Foreach([i=0,&timestamp]mutable(float hr){timestamp[i]+=hr*3600; i++;},{"hr"});
...
for(const auto &j : timestamp) {
    N->Fill(j);
    std::cout << std::setprecision(100) << j << " " << sub <<std::endl;
}

mr NAE
  • 3,144
  • 1
  • 15
  • 35