Commit 44b66db78807fe87954924e8107f58b411e9528a

Authored by Jürgen Knödlseder
1 parent 94c6da34

Increase precision in gammalib::str() floating point conversion functions and ad…

…d gammalib::rstrip_chars() function

The gammalib::str() floating point conversion functions now make sure that no precision is lost in a string conversion (well, roughly).
ChangeLog
... ... @@ -3,6 +3,7 @@
3 3 * Version 1.2.0 released
4 4 =========================
5 5  
  6 + Increase precision of gammalib::str() floating point functions
6 7 Add GFits::publish() methods (#1231)
7 8 Add GVOTable class (#1231)
8 9 Add multiplicative spectral model (#1861)
... ...
... ... @@ -50,6 +50,7 @@ The following methods have been added:
50 50 - GTime::last(const double& geolon)
51 51 - GTime::now()
52 52 - GFits::publish()
  53 +- gammalib::rstrip_chars()
53 54  
54 55 The following methods have been removed:
55 56 - <all classes>::eval_gradients()
... ... @@ -187,7 +188,12 @@ or no maps (#1828).
187 188  
188 189 13. Support module
189 190 ------------------
190   -None
  191 +The gammalib::rstrip_chars() function has been added to right strip characters
  192 +from a string.
  193 +
  194 +The gammalib::str() functions for single and double precision floating point
  195 +numbers now have an increased precision so that significant digits are no
  196 +longer lost.
191 197  
192 198  
193 199 14. XML module
... ...
include/GTools.hpp
... ... @@ -54,6 +54,8 @@ namespace gammalib {
54 54 std::string strip_whitespace(const std::string& arg);
55 55 std::string strip_chars(const std::string& arg,
56 56 const std::string& chars);
  57 + std::string rstrip_chars(const std::string& arg,
  58 + const std::string& chars);
57 59 std::string expand_env(const std::string& arg);
58 60 std::string filepath(const std::string& pathname,
59 61 const std::string& filename);
... ...
pyext/GTools.i
... ... @@ -41,6 +41,8 @@ namespace gammalib {
41 41 std::string strip_whitespace(const std::string& arg);
42 42 std::string strip_chars(const std::string& arg,
43 43 const std::string& chars);
  44 + std::string rstrip_chars(const std::string& arg,
  45 + const std::string& chars);
44 46 std::string expand_env(const std::string& arg);
45 47 std::string filepath(const std::string& pathname,
46 48 const std::string& filename);
... ...
src/app/GApplicationPar.cpp
... ... @@ -415,7 +415,7 @@ void GApplicationPar::real(const double&amp; value)
415 415 }
416 416  
417 417 // Set value string at highest precision
418   - std::string value_string = gammalib::str(value, 15);
  418 + std::string value_string = gammalib::str(value);
419 419  
420 420 // Strip trailing zeros
421 421 std::string::size_type start = 0;
... ...
src/support/GTools.cpp
... ... @@ -93,7 +93,7 @@ std::string gammalib::strip_chars(const std::string&amp; arg,
93 93 std::string::size_type start = arg.find_first_not_of(chars);
94 94 std::string::size_type stop = arg.find_last_not_of(chars);
95 95  
96   - // Continue only is start and stop are valid
  96 + // Continue only if start and stop are valid
97 97 if (start != std::string::npos && stop != std::string::npos) {
98 98  
99 99 // Continue only if stop is larger then or equal to start
... ... @@ -111,6 +111,37 @@ std::string gammalib::strip_chars(const std::string&amp; arg,
111 111  
112 112  
113 113 /***********************************************************************//**
  114 + * @brief Strip trailing character from string
  115 + *
  116 + * @param[in] arg String from which character should be stripped.
  117 + * @param[in] chars Character(s) to be stripped.
  118 + * @return String with stripped characters.
  119 + ***************************************************************************/
  120 +std::string gammalib::rstrip_chars(const std::string& arg,
  121 + const std::string& chars)
  122 +{
  123 + // Initialise empty result string
  124 + std::string result;
  125 +
  126 + // Continue only if argument is not empty
  127 + if (!arg.empty()) {
  128 +
  129 + // Get stop
  130 + std::string::size_type stop = arg.find_last_not_of(chars);
  131 +
  132 + // Continue only if stop is valid
  133 + if (stop != std::string::npos) {
  134 + result = arg.substr(0, stop+1);
  135 + }
  136 +
  137 + } // endif: argument was not empty
  138 +
  139 + // Return result
  140 + return result;
  141 +}
  142 +
  143 +
  144 +/***********************************************************************//**
114 145 * @brief Expand environment variables in string
115 146 *
116 147 * @param[in] arg String.
... ... @@ -447,21 +478,36 @@ std::string gammalib::str(const long long int&amp; value)
447 478 * @brief Convert single precision value into string
448 479 *
449 480 * @param[in] value Single precision value to be converted into string.
450   - * @param[in] precision Floating point precision (optional).
  481 + * @param[in] precision Floating point precision.
451 482 * @return String with single precision value.
  483 + *
  484 + * Converts a single precision value into a string. Any positive
  485 + * @p precision argument specifies the exact number of digits after the
  486 + * comma.
452 487 ***************************************************************************/
453 488 std::string gammalib::str(const float& value, const int& precision)
454 489 {
  490 + // Allocate output stream
455 491 std::ostringstream s_value;
  492 +
  493 + // If specified then set the requested fixed point precision. Otherwise
  494 + // use a precision that should be sufficient for floating point values.
456 495 if (precision > 0) {
457 496 s_value.precision(precision);
458 497 s_value.setf(std::ios::fixed, std::ios::floatfield);
459 498 }
460   - s_value << value;
461   - if (precision > 0) {
462   - s_value.unsetf(std::ios::floatfield);
  499 + else {
  500 + s_value.precision(7);
463 501 }
464   - return s_value.str();
  502 +
  503 + // Put floating point value in stream
  504 + s_value << value;
  505 +
  506 + // Convert to a string
  507 + std::string result = s_value.str();
  508 +
  509 + // Return result
  510 + return result;
465 511 }
466 512  
467 513  
... ... @@ -469,21 +515,36 @@ std::string gammalib::str(const float&amp; value, const int&amp; precision)
469 515 * @brief Convert double precision value into string
470 516 *
471 517 * @param[in] value Double precision value to be converted into string.
472   - * @param[in] precision Floating point precision (optional).
  518 + * @param[in] precision Floating point precision.
473 519 * @return String with double precision value.
  520 + *
  521 + * Converts a double precision value into a string. Any positive
  522 + * @p precision argument specifies the exact number of digits after the
  523 + * comma.
474 524 ***************************************************************************/
475 525 std::string gammalib::str(const double& value, const int& precision)
476 526 {
  527 + // Allocate output stream
477 528 std::ostringstream s_value;
  529 +
  530 + // If specified then set the requested fixed point precision. Otherwise
  531 + // use a precision that should be sufficient for floating point values.
478 532 if (precision > 0) {
479 533 s_value.precision(precision);
480 534 s_value.setf(std::ios::fixed, std::ios::floatfield);
481 535 }
482   - s_value << value;
483   - if (precision > 0) {
484   - s_value.unsetf(std::ios::floatfield);
  536 + else {
  537 + s_value.precision(15);
485 538 }
486   - return s_value.str();
  539 +
  540 + // Put double precision floating point value in stream
  541 + s_value << value;
  542 +
  543 + // Convert to a string
  544 + std::string result = s_value.str();
  545 +
  546 + // Return result
  547 + return result;
487 548 }
488 549  
489 550  
... ...
test/test_GMatrixSparse.cpp
1 1 /***************************************************************************
2 2 * test_GMatrixSparse.cpp - Test sparse matrix class *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2006-2015 by Juergen Knoedlseder *
  4 + * copyright (C) 2006-2016 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -1277,8 +1277,7 @@ void TestGMatrixSparse::matrix_print(void)
1277 1277  
1278 1278 // Test the print method
1279 1279 std::string output = m_test.print();
1280   - test_assert((output == reference), "Test matrix printing",
1281   - "Unexpected print() output:\n"+output+"\nExpected:\n"+reference);
  1280 + test_value(output, reference);
1282 1281  
1283 1282 // Allocate big matrix
1284 1283 GMatrixSparse big(30, 40);
... ... @@ -1297,7 +1296,7 @@ void TestGMatrixSparse::matrix_print(void)
1297 1296 ref_big.append(" Pending element ...........: (29,39)=68\n");
1298 1297 ref_big.append(" Number of allocated cells .: 1536\n");
1299 1298 ref_big.append(" Memory block size .........: 512\n");
1300   - ref_big.append(" Sparse matrix fill ........: 0.999167\n");
  1299 + ref_big.append(" Sparse matrix fill ........: 0.999166666666667\n");
1301 1300 ref_big.append(" Pending element ...........: 68\n");
1302 1301 ref_big.append(" Fill stack size ...........: 0 (none)\n");
1303 1302 ref_big.append(" 0, 1, 2, 3, 4, 5, 6, ... 33, 34, 35, 36, 37, 38, 39\n");
... ... @@ -1318,8 +1317,7 @@ void TestGMatrixSparse::matrix_print(void)
1318 1317  
1319 1318 // Test the print method
1320 1319 output = big.print();
1321   - test_assert((output == ref_big), "Test big matrix printing",
1322   - "Unexpected print() output:\n"+output+"\nExpected:\n"+ref_big);
  1320 + test_value(output, ref_big);
1323 1321  
1324 1322 // Return
1325 1323 return;
... ...
test/test_GSupport.cpp
... ... @@ -95,25 +95,25 @@ TestGSupport* TestGSupport::clone(void) const
95 95 void TestGSupport::test_tools(void)
96 96 {
97 97 // Strip whitespace
98   - test_assert(gammalib::strip_whitespace(" World ") == "World",
99   - "gammalib::strip_whitespace(\" World \")");
100   - test_assert(gammalib::strip_whitespace("World ") == "World",
101   - "gammalib::strip_whitespace(\"World \")");
102   - test_assert(gammalib::strip_whitespace(" World") == "World",
103   - "gammalib::strip_whitespace(\" World\")");
  98 + test_value(gammalib::strip_whitespace(" World "), "World");
  99 + test_value(gammalib::strip_whitespace("World "), "World");
  100 + test_value(gammalib::strip_whitespace(" World"), "World");
104 101  
105 102 // Strip characters
106   - test_assert(gammalib::strip_chars("xxWorldyy", "xy") == "World",
107   - "gammalib::strip_chars(\"xxWorldyy\")");
108   - test_assert(gammalib::strip_chars("Worldxy", "xy") == "World",
109   - "gammalib::strip_chars(\"Worldxy\")");
110   - test_assert(gammalib::strip_chars("xxWorld", "x") == "World",
111   - "gammalib::strip_chars(\"xxWorld\")");
  103 + test_value(gammalib::strip_chars("xxWorldyy", "xy"), "World");
  104 + test_value(gammalib::strip_chars("Worldxy", "xy"), "World");
  105 + test_value(gammalib::strip_chars("xxWorld", "x"), "World");
  106 +
  107 + // Right strip characters
  108 + test_value(gammalib::rstrip_chars("xxWorldyy", "xy"), "xxWorld");
  109 + test_value(gammalib::rstrip_chars("Worldxy", "xy"), "World");
  110 + test_value(gammalib::rstrip_chars("xxWorld", "x"), "xxWorld");
  111 + test_value(gammalib::rstrip_chars("0.0012300", "0"), "0.00123");
112 112  
113 113 // Test noun number conversion
114   - test_assert(gammalib::number("World", 0) == "Worlds", "Zero Worlds");
115   - test_assert(gammalib::number("World", 1) == "World", "One World");
116   - test_assert(gammalib::number("World", 2) == "Worlds", "Two Worlds");
  114 + test_value(gammalib::number("World", 0), "Worlds");
  115 + test_value(gammalib::number("World", 1), "World");
  116 + test_value(gammalib::number("World", 2), "Worlds");
117 117  
118 118 // Test XML to string conversion
119 119 std::string s_in = "Hallo World, you \"are\" my 'nice' <planet> & place";
... ... @@ -121,11 +121,9 @@ void TestGSupport::test_tools(void)
121 121 " &lt;planet&gt; &amp; place";
122 122 std::string s_out;
123 123 s_out = gammalib::str2xml(s_in);
124   - test_assert(s_out == s_ref, "gammalib::str2xml()",
125   - "Unexpected string \""+s_out+"\" (expected \""+s_ref+"\")");
  124 + test_value(s_out, s_ref);
126 125 s_out = gammalib::xml2str(s_out);
127   - test_assert(s_out == s_in, "gammalib::str2xml()",
128   - "Unexpected string \""+s_out+"\" (expected \""+s_in+"\")");
  126 + test_value(s_out, s_in);
129 127  
130 128 // Test power law flux computations
131 129 test_value(gammalib::plaw_energy_flux(2.0, 3.0, 2.5, -1.0), 2.5);
... ...
test/test_GVector.cpp
1 1 /***************************************************************************
2 2 * test_GVector.cpp - Test vector class *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2006-2013 by Juergen Knoedlseder *
  4 + * copyright (C) 2006-2016 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -382,70 +382,121 @@ void TestGVector::arithmetics(void){
382 382 "|GVector| (vector norm)");
383 383  
384 384 // min(GVector)
385   - test_assert(min(m_test)==1.1,"min(GVector)");
  385 + test_value(min(m_test), 1.1, 1e-6, "min(GVector)");
386 386  
387 387 // max(GVector)
388   - test_assert(max(m_test)==5.5,"max(GVector)");
  388 + test_value(max(m_test), 5.5, 1e-6, "max(GVector)");
389 389  
390 390 // sum(GVector)
391   - test_value(m_test[0]+m_test[1]+m_test[2]+m_test[3]+m_test[4],sum(m_test),1e-6,"sum(GVector)");
  391 + test_value(m_test[0]+m_test[1]+m_test[2]+m_test[3]+m_test[4],
  392 + sum(m_test), 1e-6, "sum(GVector)");
392 393  
393 394 // acos(GVector/10.0)
394   - test_assert(acos(m_test/10.0).print()=="(1.46057, 1.34898, 1.23449, 1.1152, 0.988432)","acos(GVector/10.0)");
  395 + GVector test = acos(m_test/10.0);
  396 + for (int i = 0; i < 5; ++i) {
  397 + test_value(test[i], std::acos(m_test[i]/10.0));
  398 + }
395 399  
396 400 // acosh(GVector)
397   - test_assert(acosh(m_test).print()=="(0.443568, 1.42542, 1.86328, 2.16158, 2.38953)","acosh(GVector)");
  401 + test = acosh(m_test);
  402 + for (int i = 0; i < 5; ++i) {
  403 + test_value(test[i], std::acosh(m_test[i]));
  404 + }
398 405  
399 406 // asin(GVector/10.0)
400   - test_assert(asin(m_test/10.0).print()=="(0.110223, 0.221814, 0.336304, 0.455599, 0.582364)","asin(GVector/10.0)");
  407 + test = asin(m_test/10.0);
  408 + for (int i = 0; i < 5; ++i) {
  409 + test_value(test[i], std::asin(m_test[i]/10.0));
  410 + }
401 411  
402 412 // asinh(GVector/10.0)
403   - test_assert(asinh(m_test/10.0).print()=="(0.109779, 0.218263, 0.324286, 0.426913, 0.52548)","asinh(GVector/10.0)");
  413 + test = asinh(m_test/10.0);
  414 + for (int i = 0; i < 5; ++i) {
  415 + test_value(test[i], std::asinh(m_test[i]/10.0));
  416 + }
404 417  
405 418 // atan(GVector/10.0)
406   - test_assert(atan(m_test/10.0).print()=="(0.10956, 0.21655, 0.318748, 0.414507, 0.502843)","atan(GVector/10.0)");
  419 + test = atan(m_test/10.0);
  420 + for (int i = 0; i < 5; ++i) {
  421 + test_value(test[i], std::atan(m_test[i]/10.0));
  422 + }
407 423  
408 424 // atanh(GVector/10.0)
409   - test_assert(atanh(m_test/10.0).print()=="(0.110447, 0.223656, 0.342828, 0.472231, 0.618381)","atanh(GVector/10.0)");
  425 + test = atanh(m_test/10.0);
  426 + for (int i = 0; i < 5; ++i) {
  427 + test_value(test[i], std::atanh(m_test[i]/10.0));
  428 + }
410 429  
411 430 // cos(GVector)
412   - test_assert(cos(m_test).print()=="(0.453596, -0.588501, -0.98748, -0.307333, 0.70867)","cos(GVector)");
  431 + test = cos(m_test);
  432 + for (int i = 0; i < 5; ++i) {
  433 + test_value(test[i], std::cos(m_test[i]));
  434 + }
413 435  
414 436 // cosh(GVector)
415   - test_assert(cosh(m_test).print()=="(1.66852, 4.56791, 13.5748, 40.7316, 122.348)","cosh(GVector)");
  437 + test = cosh(m_test);
  438 + for (int i = 0; i < 5; ++i) {
  439 + test_value(test[i], std::cosh(m_test[i]));
  440 + }
416 441  
417 442 // exp(GVector)
418   - test_assert(exp(m_test).print()=="(3.00417, 9.02501, 27.1126, 81.4509, 244.692)","exp(GVector)");
  443 + test = exp(m_test);
  444 + for (int i = 0; i < 5; ++i) {
  445 + test_value(test[i], std::exp(m_test[i]));
  446 + }
419 447  
420 448 // abs(cos(m_test))
421   - test_assert(abs(cos(m_test)).print()=="(0.453596, 0.588501, 0.98748, 0.307333, 0.70867)","abs(cos(m_test))");
  449 + test = abs(cos(m_test));
  450 + for (int i = 0; i < 5; ++i) {
  451 + test_value(test[i], std::abs(std::cos(m_test[i])));
  452 + }
422 453  
423 454 // log(GVector)
424   - test_assert(log(m_test).print()=="(0.0953102, 0.788457, 1.19392, 1.4816, 1.70475)","log(GVector)");
  455 + test = log(m_test);
  456 + for (int i = 0; i < 5; ++i) {
  457 + test_value(test[i], std::log(m_test[i]));
  458 + }
425 459  
426 460 // log10(GVector)
427   - test_assert(log10(m_test).print()=="(0.0413927, 0.342423, 0.518514, 0.643453, 0.740363)","log10(GVector)");
  461 + test = log10(m_test);
  462 + for (int i = 0; i < 5; ++i) {
  463 + test_value(test[i], std::log10(m_test[i]));
  464 + }
428 465  
429 466 // sin(GVector)
430   - test_assert(sin(m_test).print()=="(0.891207, 0.808496, -0.157746, -0.951602, -0.70554)","sin(GVector)");
  467 + test = sin(m_test);
  468 + for (int i = 0; i < 5; ++i) {
  469 + test_value(test[i], std::sin(m_test[i]));
  470 + }
431 471  
432 472 // sinh(GVector)
433   - test_assert(sinh(m_test).print()=="(1.33565, 4.45711, 13.5379, 40.7193, 122.344)","sinh(GVector)");
  473 + test = sinh(m_test);
  474 + for (int i = 0; i < 5; ++i) {
  475 + test_value(test[i], std::sinh(m_test[i]));
  476 + }
434 477  
435 478 // sqrt(GVector)
436   - test_assert(sqrt(m_test).print()=="(1.04881, 1.48324, 1.81659, 2.09762, 2.34521)","sqrt(GVector)");
  479 + test = sqrt(m_test);
  480 + for (int i = 0; i < 5; ++i) {
  481 + test_value(test[i], std::sqrt(m_test[i]));
  482 + }
437 483  
438 484 // tan(GVector)
439   - test_assert(tan(m_test).print()=="(1.96476, -1.37382, 0.159746, 3.09632, -0.995584)","tan(GVector)");
  485 + test = tan(m_test);
  486 + for (int i = 0; i < 5; ++i) {
  487 + test_value(test[i], std::tan(m_test[i]));
  488 + }
440 489  
441 490 // tanh(GVector)
442   - test_assert(tanh(m_test).print()=="(0.800499, 0.975743, 0.997283, 0.999699, 0.999967)","tanh(GVector)");
  491 + test = tanh(m_test);
  492 + for (int i = 0; i < 5; ++i) {
  493 + test_value(test[i], std::tanh(m_test[i]));
  494 + }
443 495  
444 496 // Incompatible size GVector + GVector
445 497 test_try("Incompatible size GVector + GVector:");
446 498 try {
447 499 m_result = m_test + m_bigger;
448   -
449 500 test_try_failure();
450 501 }
451 502 catch (GException::vector_mismatch &e) {
... ... @@ -459,7 +510,6 @@ void TestGVector::arithmetics(void){
459 510 test_try("cross(a,b) (using 5-dim vectors)");
460 511 try {
461 512 cross(m_test,m_test);
462   -
463 513 test_try_failure();
464 514 }
465 515 catch (GException::vector_bad_cross_dim &e) {
... ... @@ -473,7 +523,6 @@ void TestGVector::arithmetics(void){
473 523 test_try("cross(a,b) (using vectors with different dimension)");
474 524 try {
475 525 cross(m_test,m_bigger);
476   -
477 526 test_try_failure();
478 527 }
479 528 catch (GException::vector_mismatch &e) {
... ... @@ -492,7 +541,10 @@ void TestGVector::arithmetics(void){
492 541 test_cross_b[1] = 1.0;
493 542  
494 543 //Test if cross == (0,0,1)
495   - test_assert(cross(test_cross_a, test_cross_b)[0]==0&&cross(test_cross_a, test_cross_b)[1]==0&&cross(test_cross_a, test_cross_b)[2]==1,"Check cross(a,b) value");
  544 + test_assert(cross(test_cross_a, test_cross_b)[0] == 0 &&
  545 + cross(test_cross_a, test_cross_b)[1] == 0 &&
  546 + cross(test_cross_a, test_cross_b)[2] == 1,
  547 + "Check cross(a,b) value");
496 548  
497 549 test_try_success();
498 550 }
... ...