From 175f0aa944534bf19a2807e776861b54eac501c1 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.

(manually cherry picked from commit b1e4884237376fdce06265b7f96fb63e0bd8b224)
---
 src/DataTree.cc     | 12 ++++++++++--
 src/DataTree.hh     |  8 ++++----
 src/DynamicModel.cc |  6 +++---
 src/ExprNode.cc     | 10 ++++------
 src/StaticModel.cc  |  6 +++---
 5 files changed, 24 insertions(+), 18 deletions(-)

diff --git a/src/DataTree.cc b/src/DataTree.cc
index f24c3030..b315eb75 100644
--- a/src/DataTree.cc
+++ b/src/DataTree.cc
@@ -873,7 +873,7 @@ DataTree::minLagForSymbol(int symb_id) const
 }
 
 void
-DataTree::writePowerDeriv(ostream &output) const
+DataTree::writeCHelpersDefinition(ostream &output) const
 {
   if (isBinaryOpUsed(BinaryOpcode::powerDeriv))
     output << "/*" << endl
@@ -892,6 +892,12 @@ 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
@@ -915,10 +921,12 @@ DataTree::writePowerDerivJulia(ostream &output) const
 }
 
 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;
 }
 
 string
diff --git a/src/DataTree.hh b/src/DataTree.hh
index 6e9f818d..61dfcfbf 100644
--- a/src/DataTree.hh
+++ b/src/DataTree.hh
@@ -280,12 +280,12 @@ 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;
   //! Write getPowerDeriv in Julia
   void writePowerDerivJulia(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/DynamicModel.cc b/src/DynamicModel.cc
index ea98032b..f1968626 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -631,7 +631,7 @@ DynamicModel::writeDynamicPerBlockCFiles(const string &basename) const
              << endl;
 
       // Write function definition if BinaryOpcode::powerDeriv is used
-      writePowerDerivHeader(output);
+      writeCHelpersDeclaration(output);
 
       output << endl;
 
@@ -1447,7 +1447,7 @@ DynamicModel::writeDynamicCFile(const string &basename) const
          << endl;
 
   // Write function definition if BinaryOpcode::powerDeriv is used
-  writePowerDeriv(output);
+  writeCHelpersDefinition(output);
 
   output << endl;
 
@@ -1671,7 +1671,7 @@ DynamicModel::writeDynamicBlockCFile(const string &basename) const
     output << R"(#include "dynamic_)" << blk+1 << R"(.h")" << endl;
 
   output << endl;
-  writePowerDeriv(output);
+  writeCHelpersDefinition(output);
 
   output << endl
          << "void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])" << endl
diff --git a/src/ExprNode.cc b/src/ExprNode.cc
index d1ab8690..7c512ab8 100644
--- a/src/ExprNode.cc
+++ b/src/ExprNode.cc
@@ -2693,10 +2693,10 @@ UnaryOpNode::writeOutput(ostream &output, ExprNodeOutputType output_type,
         output << "abs";
       break;
     case UnaryOpcode::sign:
-      if (output_type == ExprNodeOutputType::CDynamicModel || output_type == ExprNodeOutputType::CStaticModel)
-        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;
@@ -2787,8 +2787,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 && (output_type == ExprNodeOutputType::CDynamicModel || output_type == ExprNodeOutputType::CStaticModel))
-        output << "1.0,";
       close_parenthesis = true;
     }
 
diff --git a/src/StaticModel.cc b/src/StaticModel.cc
index ba9d28f6..37cae8b1 100644
--- a/src/StaticModel.cc
+++ b/src/StaticModel.cc
@@ -311,7 +311,7 @@ StaticModel::writeStaticPerBlockCFiles(const string &basename) const
              << endl;
 
       // Write function definition if BinaryOpcode::powerDeriv is used
-      writePowerDerivHeader(output);
+      writeCHelpersDeclaration(output);
 
       output << endl;
 
@@ -1730,7 +1730,7 @@ StaticModel::writeStaticCFile(const string &basename) const
          << endl;
 
   // Write function definition if BinaryOpcode::powerDeriv is used
-  writePowerDeriv(output);
+  writeCHelpersDefinition(output);
 
   output << endl;
 
@@ -1914,7 +1914,7 @@ StaticModel::writeStaticBlockCFile(const string &basename) const
     output << R"(#include "static_)" << blk+1 << R"(.h")" << endl;
 
   output << endl;
-  writePowerDeriv(output);
+  writeCHelpersDefinition(output);
 
   output << endl
          << "void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])" << endl
-- 
GitLab