From b1e4884237376fdce06265b7f96fb63e0bd8b224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org> Date: Fri, 7 Apr 2023 15:45:44 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20sign(0)=20was=20returning=201=20?= =?UTF-8?q?instead=20of=200=20with=20use=5Fdll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The C99 copysign() function was used in the generated C output, but that function does not correctly handle zero. Replace it by a custom sign() function. --- src/DataTree.cc | 12 ++++++++++-- src/DataTree.hh | 8 ++++---- src/ExprNode.cc | 10 ++++------ src/ModelTree.hh | 38 +++++++++++++++++--------------------- src/StaticModel.cc | 2 +- 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/DataTree.cc b/src/DataTree.cc index 129d1b85..96f16214 100644 --- a/src/DataTree.cc +++ b/src/DataTree.cc @@ -890,7 +890,7 @@ DataTree::minLagForSymbol(int symb_id) const } void -DataTree::writePowerDeriv(ostream &output) const +DataTree::writeCHelpersDefinition(ostream &output) const { if (isBinaryOpUsed(BinaryOpcode::powerDeriv)) output << "/*" << endl @@ -909,13 +909,21 @@ DataTree::writePowerDeriv(ostream &output) const << " return dxp;" << endl << " }" << endl << "}" << endl; + + if (isUnaryOpUsed(UnaryOpcode::sign)) + output << "double sign(double x)" << endl + << "{" << endl + << " return (x > 0) ? 1 : ((x < 0) ? -1 : 0);" << endl + << "}" << endl; } void -DataTree::writePowerDerivHeader(ostream &output) const +DataTree::writeCHelpersDeclaration(ostream &output) const { if (isBinaryOpUsed(BinaryOpcode::powerDeriv)) output << "double getPowerDeriv(double x, double p, int k);" << endl; + if (isUnaryOpUsed(UnaryOpcode::sign)) + output << "double sign(double x);" << endl; } vector<string> diff --git a/src/DataTree.hh b/src/DataTree.hh index bfc86938..108fb253 100644 --- a/src/DataTree.hh +++ b/src/DataTree.hh @@ -277,10 +277,10 @@ public: //! Returns the minimum lag (as a negative number) of the given symbol in the whole data tree (and not only in the equations !!) /*! Returns 0 if the symbol is not used */ int minLagForSymbol(int symb_id) const; - //! Write getPowerDeriv in C (function body) - void writePowerDeriv(ostream &output) const; - //! Write getPowerDeriv in C (prototype) - void writePowerDerivHeader(ostream &output) const; + //! Writes definitions of C function helpers (getPowerDeriv(), sign()) + void writeCHelpersDefinition(ostream &output) const; + //! Writes declarations of C function helpers (getPowerDeriv(), sign()) + void writeCHelpersDeclaration(ostream &output) const; //! Thrown when trying to access an unknown variable by deriv_id class UnknownDerivIDException { diff --git a/src/ExprNode.cc b/src/ExprNode.cc index 12527dab..e69a177b 100644 --- a/src/ExprNode.cc +++ b/src/ExprNode.cc @@ -2950,10 +2950,10 @@ UnaryOpNode::writeOutput(ostream &output, ExprNodeOutputType output_type, output << "abs"; break; case UnaryOpcode::sign: - if (isCOutput(output_type)) - output << "copysign"; - else - output << "sign"; + /* C does not have a sign() function, and copysign() is not suitable + because it does not handle zero correctly, so we define our own sign() + helper function, see DataTree::writeCHelpersDefinition() */ + output << "sign"; break; case UnaryOpcode::steadyState: ExprNodeOutputType new_output_type; @@ -3054,8 +3054,6 @@ UnaryOpNode::writeOutput(ostream &output, ExprNodeOutputType output_type, && arg->precedence(output_type, temporary_terms) < precedence(output_type, temporary_terms))) { output << LEFT_PAR(output_type); - if (op_code == UnaryOpcode::sign && isCOutput(output_type)) - output << "1.0,"; close_parenthesis = true; } diff --git a/src/ModelTree.hh b/src/ModelTree.hh index ddf72dc1..f1ab64ad 100644 --- a/src/ModelTree.hh +++ b/src/ModelTree.hh @@ -973,7 +973,7 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, output << "#include <math.h>" << endl << R"(#include "mex.h")" << endl // Needed for calls to external functions << endl; - writePowerDerivHeader(output); + writeCHelpersDeclaration(output); output << endl << prototype_tt << endl << "{" << endl @@ -1012,7 +1012,7 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, output << "#include <math.h>" << endl << R"(#include "mex.h")" << endl // Needed for calls to external functions << endl; - writePowerDerivHeader(output); + writeCHelpersDeclaration(output); output << endl << prototype_main << endl << "{" << endl @@ -1043,8 +1043,7 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, output << "#include " << it.filename() << endl; output << endl; - // Write function definition if BinaryOpcode::powerDeriv is used - writePowerDeriv(output); + writeCHelpersDefinition(output); output << endl << "void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])" << endl @@ -2645,18 +2644,13 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, NB: The prefix (static/dynamic) is added to the filename (even though it’s the same source between static and dynamic) to avoid a race condition when static and dynamic are compiled in parallel. */ - open_file(model_src_dir / (prefix + "power_deriv.h")); - writePowerDerivHeader(output); - output.close(); - - filesystem::path power_deriv_src {model_src_dir / (prefix + "power_deriv.c")}; - open_file(power_deriv_src); + filesystem::path helpers_src {model_src_dir / (prefix + "helpers.c")}; + open_file(helpers_src); output << "#include <math.h>" << endl << endl; - writePowerDeriv(output); + writeCHelpersDefinition(output); output.close(); - auto power_deriv_object {compileMEX(model_src_dir, (prefix + "power_deriv"), - mexext, { power_deriv_src }, - matlabroot, false)}; + auto helpers_object {compileMEX(model_src_dir, (prefix + "helpers"), + mexext, { helpers_src }, matlabroot, false)}; size_t ttlen {0}; @@ -2724,8 +2718,9 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, open_file(source_tt); output << "#include <math.h>" << endl << R"(#include "mex.h")" << endl // Needed for calls to external functions - << R"(#include ")" << prefix << R"(power_deriv.h")" << endl - << endl + << endl; + writeCHelpersDeclaration(output); + output << endl << prototype_tt << endl << "{" << endl << tt_sparse_output[i].str() @@ -2747,7 +2742,7 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, output << "#include <math.h>" << endl << R"(#include "mex.h")" << endl // Needed for calls to external functions << endl; - writePowerDerivHeader(output); + writeCHelpersDeclaration(output); output << endl << prototype_main << endl << "{" << endl @@ -2836,7 +2831,7 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, << "}" << endl; output.close(); - vector<filesystem::path> mex_input_files { power_deriv_object, main_object_file, source_mex }; + vector<filesystem::path> mex_input_files { helpers_object, main_object_file, source_mex }; for (int j {0}; j <= i; j++) mex_input_files.push_back(tt_object_files[j]); compileMEX(mex_dir, funcname, mexext, mex_input_files, matlabroot); @@ -2864,8 +2859,9 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, open_file(source_mex); output << "#include <math.h>" << endl << R"(#include "mex.h")" << endl - << R"(#include "../)" << prefix << R"(power_deriv.h")" << endl - << endl + << endl; + writeCHelpersDeclaration(output); + output << endl << "void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])" << endl << "{" << endl << " if (nrhs != " << nargin << ")" << endl @@ -2928,7 +2924,7 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, } output << "}" << endl; output.close(); - compileMEX(block_dir, funcname, mexext, { source_mex, power_deriv_object }, matlabroot); + compileMEX(block_dir, funcname, mexext, { source_mex, helpers_object }, matlabroot); } } } diff --git a/src/StaticModel.cc b/src/StaticModel.cc index 7ff90b85..a923d322 100644 --- a/src/StaticModel.cc +++ b/src/StaticModel.cc @@ -920,7 +920,7 @@ StaticModel::writeRamseyMultipliersDerivativesCFile(const string &basename, cons output << "#include <math.h>" << endl << endl << R"(#include "mex.h")" << endl // Needed for calls to external functions << endl; - writePowerDeriv(output); + writeCHelpersDefinition(output); output << endl << "void ramsey_multipliers_static_g1(const double *restrict y, const double *restrict x, const double *restrict params, double *restrict T, double *restrict g1m_v)" << endl << "{" << endl; -- GitLab