Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changed default floating point precision to max #872

Merged
merged 11 commits into from
Apr 13, 2022
Merged

Conversation

jennuine
Copy link
Collaborator

@jennuine jennuine commented Mar 9, 2022

🎉 New feature

Closes #790

Summary

Previously, there were different precisions as discussed here: #689 (review)

This PR changes the behavior so that all floating types emitted to the output stream are maximum precision by default for consistency. A new PrintConfig::SetOutPrecision option was added so that the precision can be configured as desired.

Test it

./path/to/build/test/integration/INTEGRATION_print_config --gtest_filter=PrintConfig.OutPrecision

or with the command line tool

test.sdf
<sdf version="1.9">
<model name="model1">
  <link name="link1">
    <pose>1 1.0 -1.570796 3.14159265358979323846 0 1.57079632679489667</pose>
  </link>
</model>
</sdf>
ign sdf -p test.sdf
# output (for pose): <pose>1 1 -1.5707960000000001 3.1415926535897931 0 1.5707963267948968</pose>

ign sdf -p --precision 6 test.sdf
# output: <pose>1 1 -1.5708 3.14159 0 1.5708</pose>

ign sdf -p --precision 2 test.sdf
# output: <pose>1 1 -1.6 3.1 0 1.6</pose>

This IEEE 754 floating point converter can be used to determine expected strings: https://baseconvert.com/ieee-754-floating-point

double (64 bit) has a max of 17 digits and float (32 bit) has a max of 9 digits.

There may be a small loss of precision (~2e-16), which is related to gazebosim/gz-math#212

Checklist

  • Signed all commits for DCO
  • Added tests
  • Added example and/or tutorial
  • Updated documentation (as needed)
  • Updated migration guide (as needed)
  • codecheck passed (See contributing)
  • All tests passed (See test coverage)
  • While waiting for a review on your PR, please help review another open pull request to support the maintainers

Note to maintainers: Remember to use Squash-Merge and edit the commit message to match the pull request summary while retaining Signed-off-by messages.

@codecov-commenter
Copy link

codecov-commenter commented Mar 9, 2022

Codecov Report

Merging #872 (54056bc) into main (6c9b4ad) will increase coverage by 0.09%.
The diff coverage is 100.00%.

❗ Current head 54056bc differs from pull request most recent head f822dfd. Consider uploading reports for the commit f822dfd to get more accurate results

@@            Coverage Diff             @@
##             main     #872      +/-   ##
==========================================
+ Coverage   88.14%   88.23%   +0.09%     
==========================================
  Files         102      102              
  Lines       14706    14693      -13     
==========================================
+ Hits        12962    12964       +2     
+ Misses       1744     1729      -15     
Impacted Files Coverage Δ
include/sdf/Param.hh 77.94% <100.00%> (-0.64%) ⬇️
src/Param.cc 91.22% <100.00%> (-0.08%) ⬇️
src/PrintConfig.cc 100.00% <100.00%> (ø)
src/ign.cc 75.53% <100.00%> (+11.38%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6c9b4ad...f822dfd. Read the comment docs.

@scpeters
Copy link
Member

scpeters commented Mar 9, 2022

I see some test failures on macOS and windows in NestedModel.NestedModelWithFramesDirectComparison with an expected string not matching:

@@ -5,8 +5,8 @@
       <model name='M1'>
         <frame name='F1'>
-          <pose>0 0 0 1.5707959999999999 0 0</pose>
+          <pose>0 0 0 1.5707960000000001 0 0</pose>
         </frame>
         <frame name='F2'>
-          <pose relative_to='F1'>0 0 0 0 0.78539799999999993 0</pose>
+          <pose relative_to='F1'>0 0 0 0 0.78539800000000004 0</pose>
         </frame>
         <link name='L1'>
@@ -68,5 +68,5 @@
           </link>
         </model>
-        <pose>10 0 0 0 0 1.5708</pose>
+        <pose>10 0 0 0 0 1.5707999999999998</pose>
       </model>
     </model>

@jennuine
Copy link
Collaborator Author

jennuine commented Mar 9, 2022

I see some test failures on macOS and windows in NestedModel.NestedModelWithFramesDirectComparison with an expected string not matching:

hmm, so the values for macOS and windows were the values I was expecting but the values in linux were off by about ~2e-16. In a VC with @azeey we believed it was related to gazebosim/gz-math#212 since this loss doesn't happen with Vector3 or Color, which I left a comment here: https://github.com/ignitionrobotics/sdformat/blob/7a690da750bae9088512e21fc72b8781c66f3592/test/integration/nested_model.cc#L612-L613

I'm surprised that this loss doesn't happen in macOS or windows.. Instead of doing a direct comparison of sdfParsed and expected string, should I load the expected string into an sdf object, compare each element individually, and use EXPECT_NEAR for poses?

@EricCousineau-TRI
Copy link
Collaborator

Per VC, maybe related to -lfast-math, as in RobotLocomotion/drake#10253

@EricCousineau-TRI
Copy link
Collaborator

Per VC, suggestions:

  • Should either store only translation, or use quaternion representation to avoid math-based floating bit "noise"
  • Should check if this conversion is a fixed-point (if rpy goes from x to x', does x' still map back to x'?)

@jennuine
Copy link
Collaborator Author

jennuine commented Apr 6, 2022

The precision issue came from ign-math's Quaternion class when converting from euler to quaternion. Specifically, the output of cos function potentially differed by 1 bit between gcc/clang on Linux, MSVC x86 / MSVC x64 on Windows, and clang on macOS. For ex: https://godbolt.org/z/Tx78a3sjs

The workaround is described below.


Should either store only translation, or use quaternion representation to avoid math-based floating bit "noise"

  • For the nested_model direct string comparison tests, I created a new sdf model that uses translation only so that other tests can check the full poses: e1f6421
  • I updated the print_config test to use quaternion representation to prevent any potential future issues since there were discrepancies between MSVC versions: fa8e4a2

Now that CI is green, this PR is ready for review.

@@ -75,38 +75,48 @@ namespace sdf
struct ParamStreamer
{
const T &val;
const int precision; // Used to set std::ostream's std::setprecision
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest we use a constructor for ParamStreamer instead of the template guides below. This would ensure that precision is initialized to zero.

    ParamStreamer(const T &_val, int _precision = 0)
        : val(_val), precision(_precision) {}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion 855cb1d

};

template<class T> ParamStreamer(T) -> ParamStreamer<T>;
template<class T> ParamStreamer(T, bool) -> ParamStreamer<T>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These template guides can be removed if we create a constructor per my comment above.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -42,14 +42,16 @@ COMMANDS = { 'sdf' =>
" -d [ --describe ] [SPEC VERSION] Print the aggregated SDFormat spec description. Default version (@SDF_PROTOCOL_VERSION@).\n" +
" -g [ --graph ] <pose, frame> arg Print the PoseRelativeTo or FrameAttachedTo graph. (WARNING: This is for advanced\n" +
" use only and the output may change without any promise of stability)\n" +
" -p [ --print ] arg Print converted arg.\n" +
" -p [ --print ] arg Print converted arg. Note poses and unit vectors will be normalized.\n" +
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear what is meant by normalization of Poses. It's the quaternion representation of the rotational part of the pose that gets normalized.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src/ign.cc Outdated
@@ -134,7 +134,8 @@ extern "C" SDFORMAT_VISIBLE int cmdDescribe(const char *_version)

//////////////////////////////////////////////////
extern "C" SDFORMAT_VISIBLE int cmdPrint(const char *_path,
int inDegrees, int snapToDegrees, float snapTolerance)
int _inDegrees, int _snapToDegrees, float _snapTolerance,
bool _preserveIncludes, int _outPrecision)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it's safe to use bool in the function signature of an extern "C" function since bool is not builtin type in C. The parameter _inDegrees is technically a boolean, but we're using int for this reason, I think.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Importer.extern 'int cmdPrintPreserveIncludes(const char *)'
exit(Importer.cmdPrintPreserveIncludes(File.expand_path(options['print'])))
elsif options.key?('snap_to_degrees')
precision = -9999
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is a placeholder number for an invalid precision. Usually I see -1 used for that purpose, so seeing -9999 was a little confusing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially, I set it as -9999 since technically negative numbers in setprecision are allowed but silently ignored. I agree that this was a little confusing so I modified it to 0 and updated the -h docs to state the argument must be a positive integer: 855cb1d

@azeey
Copy link
Collaborator

azeey commented Apr 12, 2022

@osrf-jenkins run tests please

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🌱 garden Ignition Garden
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Use consistent float / double precision by using configuring the ostream object precision?
5 participants