Browse Source

Merge pull request #9506 from richardkchapman/fold_inf

HPCC-16901 ECLCC compile/link error if folding overflows

Reviewed-by: Gavin Halliday <ghalliday@hpccsystems.com>
Gavin Halliday 8 years ago
parent
commit
f2a853fb07

+ 26 - 16
common/deftype/defvalue.cpp

@@ -2022,12 +2022,22 @@ unsigned RealValue::getHash(unsigned initval)
 
 const char *RealValue::generateECL(StringBuffer &s)
 {
-    return getStringValue(s);
+    if (isinf(val))
+        return s.append("Std.Math.INFINITY");
+    else if (isnan(val))
+        return s.append("Std.Math.NaN");
+    else
+        return getStringValue(s);
 }
 
 const char *RealValue::generateCPP(StringBuffer &s, CompilerType compiler)
 {
-    return getStringValue(s);
+    if (isinf(val))
+        return s.append("rtlCreateRealInf()");
+    else if (isnan(val))
+        return s.append("rtlCreateRealNull()");
+    else
+        return getStringValue(s);
 }
 
 const char *RealValue::getStringValue(StringBuffer &s)
@@ -2614,7 +2624,7 @@ IValue * divideValues(IValue * left, IValue * right, byte dbz)
         if (rv)
             res = lv / rv;
         else if (dbz == DBZnan)
-            res =  rtlCreateRealNull();
+            res =  rtlCreateRealInf();
         else
             res = 0.0;
 
@@ -2843,9 +2853,9 @@ IValue * truncateValue(IValue * v)
     return NULL;
 }
 
-IValue * lnValue(IValue * v)
+IValue * lnValue(IValue * v, byte onZero)
 {
-    return createRealValue(rtlLog(v->getRealValue()), 8);
+    return createRealValue(rtlLog(v->getRealValue(), onZero), 8);
 }
 
 IValue * sinValue(IValue * v)
@@ -2878,14 +2888,14 @@ IValue * tanhValue(IValue * v)
     return createRealValue(tanh(v->getRealValue()), 8);
 }
 
-IValue * asinValue(IValue * v)
+IValue * asinValue(IValue * v, byte onZero)
 {
-    return createRealValue(rtlASin(v->getRealValue()), 8);
+    return createRealValue(rtlASin(v->getRealValue(), onZero), 8);
 }
 
-IValue * acosValue(IValue * v)
+IValue * acosValue(IValue * v, byte onZero)
 {
-    return createRealValue(rtlACos(v->getRealValue()), 8);
+    return createRealValue(rtlACos(v->getRealValue(), onZero), 8);
 }
 
 IValue * atanValue(IValue * v)
@@ -2898,23 +2908,23 @@ IValue * atan2Value(IValue * y, IValue* x)
     return createRealValue(atan2(y->getRealValue(), x->getRealValue()), 8);
 }
 
-IValue * log10Value(IValue * v)
+IValue * log10Value(IValue * v, byte onZero)
 {
-    return createRealValue(rtlLog10(v->getRealValue()), 8);
+    return createRealValue(rtlLog10(v->getRealValue(), onZero), 8);
 }
 
-IValue * sqrtValue(IValue * v)
+IValue * sqrtValue(IValue * v, byte onZero)
 {
     switch(v->getTypeCode())
     {
+    case type_decimal:
+        //MORE: This should probably do this more accurately.
+        //fall into
     case type_int:
     case type_swapint:
     case type_packedint:
     case type_real:
-        return createRealValue(rtlSqrt(v->getRealValue()), 8);
-    case type_decimal:
-        //MORE: This should probably do this more accurately.
-        return createRealValue(rtlSqrt(v->getRealValue()), 8);
+        return createRealValue(rtlSqrt(v->getRealValue(), onZero), 8);
     }
     throwUnexpected();
     return NULL;

+ 5 - 5
common/deftype/defvalue.hpp

@@ -121,19 +121,19 @@ extern DEFTYPE_API IValue * concatValues(IValue * left, IValue * right);
 extern DEFTYPE_API IValue * negateValue(IValue * v);
 extern DEFTYPE_API IValue * expValue(IValue * v);
 extern DEFTYPE_API IValue * truncateValue(IValue * v);
-extern DEFTYPE_API IValue * lnValue(IValue * v);
+extern DEFTYPE_API IValue * lnValue(IValue * v, byte onZero);
 extern DEFTYPE_API IValue * sinValue(IValue * v);
 extern DEFTYPE_API IValue * cosValue(IValue * v);
 extern DEFTYPE_API IValue * tanValue(IValue * v);
 extern DEFTYPE_API IValue * sinhValue(IValue * v);
 extern DEFTYPE_API IValue * coshValue(IValue * v);
 extern DEFTYPE_API IValue * tanhValue(IValue * v);
-extern DEFTYPE_API IValue * asinValue(IValue * v);
-extern DEFTYPE_API IValue * acosValue(IValue * v);
+extern DEFTYPE_API IValue * asinValue(IValue * v, byte onZero);
+extern DEFTYPE_API IValue * acosValue(IValue * v, byte onZero);
 extern DEFTYPE_API IValue * atanValue(IValue * v);
 extern DEFTYPE_API IValue * atan2Value(IValue * y, IValue* x);
-extern DEFTYPE_API IValue * log10Value(IValue * v);
-extern DEFTYPE_API IValue * sqrtValue(IValue * v);
+extern DEFTYPE_API IValue * log10Value(IValue * v, byte onZero);
+extern DEFTYPE_API IValue * sqrtValue(IValue * v, byte onZero);
 extern DEFTYPE_API IValue * absValue(IValue * v);
 extern DEFTYPE_API IValue * roundValue(IValue * v);
 extern DEFTYPE_API IValue * roundToValue(IValue * v, int places);

+ 17 - 6
ecl/hql/hqlfold.cpp

@@ -36,6 +36,7 @@
 #include "hqlfold.hpp"
 #include "hqlthql.hpp"
 #include "eclhelper.hpp"
+#include "math.h"
 
 #ifdef __APPLE__
 #include <dlfcn.h>
@@ -2410,6 +2411,7 @@ static IHqlExpression * foldHashXX(IHqlExpression * expr)
 
 IHqlExpression * foldConstantOperator(IHqlExpression * expr, unsigned foldOptions, ITemplateContext * templateContext)
 {
+    DBZaction onZero = (foldOptions & HFOforcefold) ? DBZfail : DBZnone;
     node_operator op = expr->getOperator();
     switch (op)
     {
@@ -2571,7 +2573,6 @@ IHqlExpression * foldConstantOperator(IHqlExpression * expr, unsigned foldOption
             IValue * rightValue = expr->queryChild(1)->queryValue();
             if (leftValue && rightValue)
             {
-                DBZaction onZero = (foldOptions & HFOforcefold) ? DBZfail : DBZnone;
                 IValue * res;
                 if (op == no_div)
                     res = divideValues(leftValue, rightValue, onZero);
@@ -3107,7 +3108,9 @@ IHqlExpression * foldConstantOperator(IHqlExpression * expr, unsigned foldOption
                 case no_exp:
                     return createConstant(expValue(constValue));
                 case no_ln:
-                    return createConstant(lnValue(constValue));
+                    if (onZero == DBZnone && constValue->getRealValue() <= 0)
+                        break;
+                    return createConstant(lnValue(constValue, onZero));
                 case no_sin:
                     return createConstant(sinValue(constValue));
                 case no_cos:
@@ -3115,9 +3118,13 @@ IHqlExpression * foldConstantOperator(IHqlExpression * expr, unsigned foldOption
                 case no_tan:
                     return createConstant(tanValue(constValue));
                 case no_asin:
-                    return createConstant(asinValue(constValue));
+                    if (onZero == DBZnone && fabs(constValue->getRealValue()) > 1.0)
+                        break;
+                    return createConstant(asinValue(constValue, onZero));
                 case no_acos:
-                    return createConstant(acosValue(constValue));
+                    if (onZero == DBZnone && fabs(constValue->getRealValue()) > 1.0)
+                        break;
+                    return createConstant(acosValue(constValue, onZero));
                 case no_atan:
                     return createConstant(atanValue(constValue));
                 case no_sinh:
@@ -3127,9 +3134,13 @@ IHqlExpression * foldConstantOperator(IHqlExpression * expr, unsigned foldOption
                 case no_tanh:
                     return createConstant(tanhValue(constValue));
                 case no_log10:
-                    return createConstant(log10Value(constValue));
+                    if (onZero == DBZnone && constValue->getRealValue() <= 0)
+                        break;
+                    return createConstant(log10Value(constValue, onZero));
                 case no_sqrt:
-                    return createConstant(sqrtValue(constValue));
+                    if (onZero == DBZnone && constValue->getRealValue() < 0)
+                        break;
+                    return createConstant(sqrtValue(constValue, onZero));
                 case no_abs:
                     return createConstant(absValue(constValue));
                 }

+ 2 - 0
ecl/hqlcpp/hqlcatom.cpp

@@ -204,6 +204,7 @@ IIdAtom * createQStrRangeHighId;
 IIdAtom * createQuotedStringId;
 IIdAtom * createRangeLowId;
 IIdAtom * createRangeHighId;
+IIdAtom * createRealInfId;
 IIdAtom * createRealNullId;
 IIdAtom * createRowFromJsonId;
 IIdAtom * createRowFromXmlId;
@@ -861,6 +862,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM-1)
     MAKEID(createQuotedString);
     MAKEID(createRangeLow);
     MAKEID(createRangeHigh);
+    MAKEID(createRealInf);
     MAKEID(createRealNull);
     MAKEID(createRegex);
     MAKEID(createRowFromJson);

+ 1 - 0
ecl/hqlcpp/hqlcatom.hpp

@@ -202,6 +202,7 @@ extern IIdAtom * createQStrRangeHighId;
 extern IIdAtom * createQuotedStringId;
 extern IIdAtom * createRangeLowId;
 extern IIdAtom * createRangeHighId;
+extern IIdAtom * createRealInfId;
 extern IIdAtom * createRealNullId;
 extern IIdAtom * createRowFromJsonId;
 extern IIdAtom * createRowFromXmlId;

+ 9 - 7
ecl/hqlcpp/hqlcpp.cpp

@@ -3091,7 +3091,7 @@ void HqlCppTranslator::buildExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlBoun
         doBuildExprSysFunc(ctx, expr, tgt, clibExpId);
         return;
     case no_ln:
-        doBuildExprSysFunc(ctx, expr, tgt, lnId);
+        doBuildExprSysFunc(ctx, expr, tgt, lnId, options.divideByZeroAction);
         return;
     case no_sin:
         doBuildExprSysFunc(ctx, expr, tgt, sinId);
@@ -3103,10 +3103,10 @@ void HqlCppTranslator::buildExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlBoun
         doBuildExprSysFunc(ctx, expr, tgt, tanId);
         return;
     case no_asin:
-        doBuildExprSysFunc(ctx, expr, tgt, asinId);
+        doBuildExprSysFunc(ctx, expr, tgt, asinId, options.divideByZeroAction);
         return;
     case no_acos:
-        doBuildExprSysFunc(ctx, expr, tgt, acosId);
+        doBuildExprSysFunc(ctx, expr, tgt, acosId, options.divideByZeroAction);
         return;
     case no_atan:
         doBuildExprSysFunc(ctx, expr, tgt, atanId);
@@ -3124,7 +3124,7 @@ void HqlCppTranslator::buildExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlBoun
         doBuildExprSysFunc(ctx, expr, tgt, tanhId);
         return;
     case no_log10:
-        doBuildExprSysFunc(ctx, expr, tgt, log10Id);
+        doBuildExprSysFunc(ctx, expr, tgt, log10Id, options.divideByZeroAction);
         return;
     case no_power:
         doBuildExprSysFunc(ctx, expr, tgt, powerId);
@@ -3153,7 +3153,7 @@ void HqlCppTranslator::buildExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlBoun
         doBuildExprRound(ctx, expr, tgt);
         return;
     case no_sqrt:
-        doBuildExprSysFunc(ctx, expr, tgt, sqrtId);
+        doBuildExprSysFunc(ctx, expr, tgt, sqrtId, options.divideByZeroAction);
         return;
     case no_truncate:
         doBuildExprTrunc(ctx, expr, tgt);
@@ -7348,7 +7348,7 @@ void HqlCppTranslator::doBuildDivideByZero(BuildCtx & ctx, const CHqlBoundTarget
             if (zero->queryType()->getTypeCode() == type_real)
             {
                 HqlExprArray noArgs;
-                nan.setown(bindFunctionCall(createRealNullId, noArgs));
+                nan.setown(bindFunctionCall(createRealInfId, noArgs));
             }
 
             if (target)
@@ -9601,7 +9601,7 @@ void HqlCppTranslator::doBuildAssignEventExtra(BuildCtx & ctx, const CHqlBoundTa
 //---------------------------------------------------------------------------
 //-- system call e.g. EXP(), LOG()...
 
-void HqlCppTranslator::doBuildExprSysFunc(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, IIdAtom * funcName)
+void HqlCppTranslator::doBuildExprSysFunc(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, IIdAtom * funcName, byte dbz)
 {
     HqlExprArray args;
     ForEachChild(i, expr)
@@ -9610,6 +9610,8 @@ void HqlCppTranslator::doBuildExprSysFunc(BuildCtx & ctx, IHqlExpression * expr,
         if (!cur->isAttribute())
             args.append(*LINK(cur));
     }
+    if (dbz)
+        args.append(*createConstant(dbz));
     OwnedHqlExpr call = bindFunctionCall(funcName, args);
     buildExpr(ctx, call, tgt);
 }

+ 1 - 1
ecl/hqlcpp/hqlcpp.ipp

@@ -1377,7 +1377,7 @@ public:
     void doBuildExprSelect(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt);
     void doBuildExprSizeof(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt);
     void doBuildExprSubString(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt);
-    void doBuildExprSysFunc(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, IIdAtom * funcName);
+    void doBuildExprSysFunc(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt, IIdAtom * funcName, byte dbz = 0);
     void doBuildExprTransfer(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt);
     void doBuildExprTrim(BuildCtx & ctx, IHqlExpression * target, CHqlBoundExpr & tgt);
     void doBuildExprTrunc(BuildCtx & ctx, IHqlExpression * target, CHqlBoundExpr & tgt);

+ 6 - 5
ecl/hqlcpp/hqlcppsys.ecl

@@ -406,6 +406,7 @@ const char * cppSystemText[]  = {
 
     "   boolean validReal(const data src) : eclrtl,pure,library='eclrtl',entrypoint='rtlIsValidReal';",
     "   real8 createRealNull() : eclrtl,pure,library='eclrtl',entrypoint='rtlCreateRealNull';",
+    "   real8 createRealInf() : eclrtl,pure,library='eclrtl',entrypoint='rtlCreateRealInf';",
 
     "   deserializeRaw(noconst data field, boolean o) : eclrtl,include='eclrtl.hpp',library='eclrtl',entrypoint='deserializeRaw';",
     "   data deserializeDataX(boolean o) :  eclrtl,include='eclrtl.hpp',library='eclrtl',entrypoint='deserializeDataX';",
@@ -569,20 +570,20 @@ const char * cppSystemText[]  = {
     "   unsigned4 strlen(const varstring1 src) :    sys,pure,entrypoint='strlen';",
 
     "   real8 clibExp(real8 arg) :  sys,pure,entrypoint='exp';",
-    "   real8 _LN(real8 arg) :  eclrtl,pure,library='eclrtl',entrypoint='rtlLog';",
+    "   real8 _LN(real8 arg, unsigned1 dbz=0) :  eclrtl,pure,library='eclrtl',entrypoint='rtlLog';",
     "   real8 _SIN(real8 arg) : sys,pure,entrypoint='sin';",
     "   real8 _COS(real8 arg) : sys,pure,entrypoint='cos';",
     "   real8 _TAN(real8 arg) : sys,pure,entrypoint='tan';",
     "   real8 _SINH(real8 arg) :    sys,pure,entrypoint='sinh';",
     "   real8 _COSH(real8 arg) :    sys,pure,entrypoint='cosh';",
     "   real8 _TANH(real8 arg) :    sys,pure,entrypoint='tanh';",
-    "   real8 _ASIN(real8 arg) :    eclrtl,pure,library='eclrtl',entrypoint='rtlASin';",
-    "   real8 _ACOS(real8 arg) :    eclrtl,pure,library='eclrtl',entrypoint='rtlACos';",
+    "   real8 _ASIN(real8 arg, unsigned1 dbz=0) :    eclrtl,pure,library='eclrtl',entrypoint='rtlASin';",
+    "   real8 _ACOS(real8 arg, unsigned1 dbz=0) :    eclrtl,pure,library='eclrtl',entrypoint='rtlACos';",
     "   real8 _ATAN(real8 arg) :    sys,pure,entrypoint='atan';",
     "   real8 _ATAN2(real8 y, real8 x) :    sys,pure,entrypoint='atan2';",
-    "   real8 _LOG10(real8 arg) :   eclrtl,pure,library='eclrtl',entrypoint='rtlLog10';",
+    "   real8 _LOG10(real8 arg, unsigned1 dbz=0) :   eclrtl,pure,library='eclrtl',entrypoint='rtlLog10';",
     "   real8 _POWER(real8 x, real8 y) :    sys,pure,entrypoint='pow';",
-    "   real8 _SQRT(real8 x) :  eclrtl,pure,library='eclrtl',entrypoint='rtlSqrt';",
+    "   real8 _SQRT(real8 x, unsigned1 dbz=0) :  eclrtl,pure,library='eclrtl',entrypoint='rtlSqrt';",
 
     "   writeEbcdic(const ebcdic string src) : method,include='eclhelper.hpp',entrypoint='writeString';",
     "   writeReal(real8 src) : method,include='eclhelper.hpp',entrypoint='writeReal';",

+ 1 - 0
ecllibrary/std/CMakeLists.txt

@@ -23,6 +23,7 @@ set(
     BundleBase.ecl
     Date.ecl
     File.ecl
+    Math.ecl
     Metaphone3.ecl
     Metaphone.ecl
     Str.ecl

+ 55 - 0
ecllibrary/std/Math.ecl

@@ -0,0 +1,55 @@
+/*##############################################################################
+## HPCC SYSTEMS software Copyright (C) 2017 HPCC Systems®.  All rights reserved.
+############################################################################## */
+
+
+rtl := 
+    SERVICE : fold
+REAL8 Infinity() : eclrtl,pure,include,library='eclrtl',entrypoint='rtlCreateRealInf';
+REAL8 Nan() : eclrtl,pure,include,library='eclrtl',entrypoint='rtlCreateRealNull';
+BOOLEAN IsInfinite(REAL8 value) : eclrtl,pure,include,library='eclrtl',entrypoint='rtlIsInfinite';
+BOOLEAN IsNaN(REAL8 value) : eclrtl,pure,include,library='eclrtl',entrypoint='rtlIsNaN';
+BOOLEAN IsFinite(REAL8 value) : eclrtl,pure,include,library='eclrtl',entrypoint='rtlIsFinite';
+    END;
+
+EXPORT Math := MODULE
+
+/**
+ * Return a real "infinity" value.
+ * 
+ */
+ 
+EXPORT REAL8 Infinity := rtl.Infinity();
+
+/**
+ * Return a non-signalling NaN (Not a Number)value.
+ * 
+ */
+ 
+EXPORT REAL8 NaN := rtl.NaN();
+
+/**
+ * Return whether a real value is infinite (positive or negative).
+ * 
+ * @param val           The value to test.
+ */
+
+EXPORT BOOLEAN isInfinite(REAL8 val) := rtl.isInfinite(val);
+
+/**
+ * Return whether a real value is a NaN (not a number) value.
+ * 
+ * @param val           The value to test.
+ */
+
+EXPORT BOOLEAN isNaN(REAL8 val) := rtl.isNaN(val);
+
+/**
+ * Return whether a real value is a valid value (neither infinite not NaN).
+ * 
+ * @param val           The value to test.
+ */
+
+EXPORT BOOLEAN isFinite(REAL8 val) := rtl.isFinite(val);
+
+END;

+ 42 - 0
ecllibrary/teststd/Math/TestInfinity.ecl

@@ -0,0 +1,42 @@
+/*##############################################################################
+## HPCC SYSTEMS software Copyright (C) 2017 HPCC Systems®.  All rights reserved.
+############################################################################## */
+
+IMPORT Std.Math;
+
+dbz := #option('divideByZero', 'nan');
+
+EXPORT TestInfinity := MODULE
+
+  EXPORT TestConstant := MODULE
+    EXPORT TestInf1 := WHEN(ASSERT(Math.IsInfinite(Math.Infinity), CONST), dbz);
+    EXPORT TestInf2 := ASSERT(Math.IsInfinite(-Math.Infinity), CONST);
+    EXPORT TestInf3 := ASSERT(NOT Math.IsNan(Math.Infinity), CONST);
+    EXPORT TestInf4 := ASSERT(NOT Math.IsFinite(Math.Infinity), CONST);
+
+    EXPORT TestNan1 := ASSERT(NOT Math.IsInfinite(Math.NaN), CONST);
+    EXPORT TestNan2 := ASSERT(Math.IsNan(Math.NaN), CONST);
+    EXPORT TestNan3 := ASSERT(NOT Math.IsFinite(Math.NaN), CONST);
+
+    EXPORT TestNorm1 := ASSERT(NOT Math.IsInfinite(0.0), CONST);
+    EXPORT TestNorm2 := ASSERT(NOT Math.IsNan(0.0), CONST);
+    EXPORT TestNorm3 := ASSERT(Math.IsFinite(0.0), CONST);
+  END;
+
+  EXPORT TestVariable := MODULE
+    SHARED zero := nofold(0.0);
+    EXPORT TestInf1 := ASSERT(Math.IsInfinite(1.0/zero));
+    EXPORT TestInf2 := ASSERT(Math.IsInfinite(log(zero)));
+    EXPORT TestInf3 := ASSERT(NOT Math.IsNan(2.0/zero));
+    EXPORT TestInf4 := ASSERT(NOT Math.IsFinite(3.0/zero));
+
+    EXPORT TestNan1 := ASSERT(NOT Math.IsInfinite(log(zero - 1.0)));
+    EXPORT TestNan2 := ASSERT(Math.IsNan(log(zero - 2.0)));
+    EXPORT TestNan3 := ASSERT(NOT Math.IsFinite(log(zero - 3.0)));
+
+    EXPORT TestNorm1 := ASSERT(NOT Math.IsInfinite(zero));
+    EXPORT TestNorm2 := ASSERT(NOT Math.IsNan(zero));
+    EXPORT TestNorm3 := ASSERT(Math.IsFinite(zero));
+  END;
+
+END;

+ 59 - 10
rtl/eclrtl/eclrtl.cpp

@@ -4183,33 +4183,65 @@ ECLRTL_API void serializeReal8(double field, MemoryBuffer &out)
 
 //These maths functions can all have out of range arguments....
 //---------------------------------------------------------------------------
-ECLRTL_API double rtlLog10(double x)
+static double rtlInvalidArgument(DBZaction dbz, const char *source, double arg)
 {
-    if (x <= 0) return 0;
+    switch ((DBZaction) dbz)
+    {
+    case DBZfail:
+        throw MakeStringException(MSGAUD_user, -1, "Invalid argument to %s: %f", source, arg);
+    case DBZnan:
+        return rtlCreateRealNull();
+    }
+    return 0;
+}
+
+static double rtlInvalidLog(DBZaction dbz, const char *source, double arg)
+{
+    switch ((DBZaction) dbz)
+    {
+    case DBZfail:
+        throw MakeStringException(MSGAUD_user, -1, "Invalid argument to %s: %f", source, arg);
+    case DBZnan:
+        if (arg)
+            return rtlCreateRealNull();
+        else
+            return -INFINITY;
+    }
+    return 0;
+}
+
+ECLRTL_API double rtlLog10(double x, byte dbz)
+{
+    if (x <= 0)
+        return rtlInvalidLog((DBZaction) dbz, "LOG10", x);
     return log10(x);
 }
 
-ECLRTL_API double rtlLog(double x)
+ECLRTL_API double rtlLog(double x, byte dbz)
 {
-    if (x <= 0) return 0;
+    if (x <= 0)
+        return rtlInvalidLog((DBZaction) dbz, "LOG10", x);
     return log(x);
 }
 
-ECLRTL_API double rtlSqrt(double x)
+ECLRTL_API double rtlSqrt(double x, byte dbz)
 {
-    if (x < 0) return 0;
+    if (x < 0)
+        return rtlInvalidArgument((DBZaction) dbz, "SQRT", x);
     return sqrt(x);
 }
 
-ECLRTL_API double rtlACos(double x)
+ECLRTL_API double rtlACos(double x, byte dbz)
 {
-    if (fabs(x) > 1) return 0;
+    if (fabs(x) > 1)
+        return rtlInvalidArgument((DBZaction) dbz, "ACOS", x);
     return acos(x);
 }
 
-ECLRTL_API double rtlASin(double x)
+ECLRTL_API double rtlASin(double x, byte dbz)
 {
-    if (fabs(x) > 1) return 0;
+    if (fabs(x) > 1)
+        return rtlInvalidArgument((DBZaction) dbz, "ASIN", x);
     return asin(x);
 }
 
@@ -4264,6 +4296,23 @@ double rtlCreateRealNull()
     return u.r;
 }
 
+double rtlCreateRealInf()
+{
+    return INFINITY;
+}
+
+bool rtlIsInfinite(double value)
+{
+    return isinf(value);
+}
+bool rtlIsNaN(double value)
+{
+    return isnan(value);
+}
+bool rtlIsFinite(double value)
+{
+    return isfinite(value);
+}
 
 void rtlUnicodeToUnicode(size32_t outlen, UChar * out, size32_t inlen, UChar const *in)
 {

+ 9 - 5
rtl/eclrtl/eclrtl.hpp

@@ -595,14 +595,18 @@ ECLRTL_API void serializeReal4(float field, MemoryBuffer &out);
 ECLRTL_API void serializeReal8(double field, MemoryBuffer &out);
 
 //These maths functions can all have out of range arguments....
-ECLRTL_API double rtlLog(double x);
-ECLRTL_API double rtlLog10(double x);
-ECLRTL_API double rtlSqrt(double x);
-ECLRTL_API double rtlACos(double x);
-ECLRTL_API double rtlASin(double x);
+ECLRTL_API double rtlLog(double x, byte dbz = DBZzero);
+ECLRTL_API double rtlLog10(double x, byte dbz = DBZzero);
+ECLRTL_API double rtlSqrt(double x, byte dbz = DBZzero);
+ECLRTL_API double rtlACos(double x, byte dbz = DBZzero);
+ECLRTL_API double rtlASin(double x, byte dbz = DBZzero);
 
 ECLRTL_API bool rtlIsValidReal(unsigned size, const void * data);
 ECLRTL_API double rtlCreateRealNull();
+ECLRTL_API double rtlCreateRealInf();
+ECLRTL_API bool rtlIsInfinite(double value);
+ECLRTL_API bool rtlIsNaN(double value);
+ECLRTL_API bool rtlIsFinite(double value);
 
 ECLRTL_API unsigned rtlQStrLength(unsigned size);
 ECLRTL_API unsigned rtlQStrSize(unsigned length);

+ 90 - 0
testing/regress/ecl/key/zerodivide1.xml

@@ -0,0 +1,90 @@
+<Dataset name='Result 1'>
+ <Row><Result_1>log10(0) = </Result_1></Row>
+</Dataset>
+<Dataset name='Result 2'>
+ <Row><Result_2>-inf</Result_2></Row>
+</Dataset>
+<Dataset name='Result 3'>
+ <Row><Result_3>-inf</Result_3></Row>
+</Dataset>
+<Dataset name='Result 4'>
+ <Row><Result_4>log(0) = </Result_4></Row>
+</Dataset>
+<Dataset name='Result 5'>
+ <Row><Result_5>-inf</Result_5></Row>
+</Dataset>
+<Dataset name='Result 6'>
+ <Row><Result_6>-inf</Result_6></Row>
+</Dataset>
+<Dataset name='Result 7'>
+ <Row><Result_7>log10(-1) = </Result_7></Row>
+</Dataset>
+<Dataset name='Result 8'>
+ <Row><Result_8>nan</Result_8></Row>
+</Dataset>
+<Dataset name='Result 9'>
+ <Row><Result_9>nan</Result_9></Row>
+</Dataset>
+<Dataset name='Result 10'>
+ <Row><Result_10>log(-1) = </Result_10></Row>
+</Dataset>
+<Dataset name='Result 11'>
+ <Row><Result_11>nan</Result_11></Row>
+</Dataset>
+<Dataset name='Result 12'>
+ <Row><Result_12>nan</Result_12></Row>
+</Dataset>
+<Dataset name='Result 13'>
+ <Row><Result_13>sqrt(-1) = </Result_13></Row>
+</Dataset>
+<Dataset name='Result 14'>
+ <Row><Result_14>nan</Result_14></Row>
+</Dataset>
+<Dataset name='Result 15'>
+ <Row><Result_15>nan</Result_15></Row>
+</Dataset>
+<Dataset name='Result 16'>
+ <Row><Result_16>1/0 = </Result_16></Row>
+</Dataset>
+<Dataset name='Result 17'>
+ <Row><Result_17>inf</Result_17></Row>
+</Dataset>
+<Dataset name='Result 18'>
+ <Row><Result_18>inf</Result_18></Row>
+</Dataset>
+<Dataset name='Result 19'>
+ <Row><Result_19>fmod(1.0, 0.0) = </Result_19></Row>
+</Dataset>
+<Dataset name='Result 20'>
+ <Row><Result_20>0</Result_20></Row>
+</Dataset>
+<Dataset name='Result 21'>
+ <Row><Result_21>0</Result_21></Row>
+</Dataset>
+<Dataset name='Result 22'>
+ <Row><Result_22>fmod(5.3, 2.0) = </Result_22></Row>
+</Dataset>
+<Dataset name='Result 23'>
+ <Row><Result_23>1</Result_23></Row>
+</Dataset>
+<Dataset name='Result 24'>
+ <Row><Result_24>1</Result_24></Row>
+</Dataset>
+<Dataset name='Result 25'>
+ <Row><Result_25>acos(2) = </Result_25></Row>
+</Dataset>
+<Dataset name='Result 26'>
+ <Row><Result_26>nan</Result_26></Row>
+</Dataset>
+<Dataset name='Result 27'>
+ <Row><Result_27>nan</Result_27></Row>
+</Dataset>
+<Dataset name='Result 28'>
+ <Row><Result_28>asin(2) = </Result_28></Row>
+</Dataset>
+<Dataset name='Result 29'>
+ <Row><Result_29>nan</Result_29></Row>
+</Dataset>
+<Dataset name='Result 30'>
+ <Row><Result_30>nan</Result_30></Row>
+</Dataset>

+ 90 - 0
testing/regress/ecl/key/zerodivide2.xml

@@ -0,0 +1,90 @@
+<Dataset name='Result 1'>
+ <Row><Result_1>log10(0) = </Result_1></Row>
+</Dataset>
+<Dataset name='Result 2'>
+ <Row><Result_2>0.0</Result_2></Row>
+</Dataset>
+<Dataset name='Result 3'>
+ <Row><Result_3>0.0</Result_3></Row>
+</Dataset>
+<Dataset name='Result 4'>
+ <Row><Result_4>log(0) = </Result_4></Row>
+</Dataset>
+<Dataset name='Result 5'>
+ <Row><Result_5>0.0</Result_5></Row>
+</Dataset>
+<Dataset name='Result 6'>
+ <Row><Result_6>0.0</Result_6></Row>
+</Dataset>
+<Dataset name='Result 7'>
+ <Row><Result_7>log10(-1) = </Result_7></Row>
+</Dataset>
+<Dataset name='Result 8'>
+ <Row><Result_8>0.0</Result_8></Row>
+</Dataset>
+<Dataset name='Result 9'>
+ <Row><Result_9>0.0</Result_9></Row>
+</Dataset>
+<Dataset name='Result 10'>
+ <Row><Result_10>log(-1) = </Result_10></Row>
+</Dataset>
+<Dataset name='Result 11'>
+ <Row><Result_11>0.0</Result_11></Row>
+</Dataset>
+<Dataset name='Result 12'>
+ <Row><Result_12>0.0</Result_12></Row>
+</Dataset>
+<Dataset name='Result 13'>
+ <Row><Result_13>sqrt(-1) = </Result_13></Row>
+</Dataset>
+<Dataset name='Result 14'>
+ <Row><Result_14>0.0</Result_14></Row>
+</Dataset>
+<Dataset name='Result 15'>
+ <Row><Result_15>0.0</Result_15></Row>
+</Dataset>
+<Dataset name='Result 16'>
+ <Row><Result_16>1/0 = </Result_16></Row>
+</Dataset>
+<Dataset name='Result 17'>
+ <Row><Result_17>0.0</Result_17></Row>
+</Dataset>
+<Dataset name='Result 18'>
+ <Row><Result_18>0.0</Result_18></Row>
+</Dataset>
+<Dataset name='Result 19'>
+ <Row><Result_19>fmod(1.0, 0.0) = </Result_19></Row>
+</Dataset>
+<Dataset name='Result 20'>
+ <Row><Result_20>0</Result_20></Row>
+</Dataset>
+<Dataset name='Result 21'>
+ <Row><Result_21>0</Result_21></Row>
+</Dataset>
+<Dataset name='Result 22'>
+ <Row><Result_22>fmod(5.3, 2.0) = </Result_22></Row>
+</Dataset>
+<Dataset name='Result 23'>
+ <Row><Result_23>1</Result_23></Row>
+</Dataset>
+<Dataset name='Result 24'>
+ <Row><Result_24>1</Result_24></Row>
+</Dataset>
+<Dataset name='Result 25'>
+ <Row><Result_25>acos(2) = </Result_25></Row>
+</Dataset>
+<Dataset name='Result 26'>
+ <Row><Result_26>0.0</Result_26></Row>
+</Dataset>
+<Dataset name='Result 27'>
+ <Row><Result_27>0.0</Result_27></Row>
+</Dataset>
+<Dataset name='Result 28'>
+ <Row><Result_28>asin(2) = </Result_28></Row>
+</Dataset>
+<Dataset name='Result 29'>
+ <Row><Result_29>0.0</Result_29></Row>
+</Dataset>
+<Dataset name='Result 30'>
+ <Row><Result_30>0.0</Result_30></Row>
+</Dataset>

+ 18 - 0
testing/regress/ecl/key/zerodivide3.xml

@@ -0,0 +1,18 @@
+<Dataset name='Result 1'>
+ <Row><Result_1>123.0</Result_1></Row>
+</Dataset>
+<Dataset name='Result 2'>
+ <Row><Result_2>124.0</Result_2></Row>
+</Dataset>
+<Dataset name='Result 3'>
+ <Row><Result_3>125.0</Result_3></Row>
+</Dataset>
+<Dataset name='Result 4'>
+ <Row><Result_4>126.0</Result_4></Row>
+</Dataset>
+<Dataset name='Result 5'>
+ <Row><Result_5>127.0</Result_5></Row>
+</Dataset>
+<Dataset name='Result 6'>
+ <Row><Result_6>128.0</Result_6></Row>
+</Dataset>

+ 33 - 0
testing/regress/ecl/zerodivide1.ecl

@@ -0,0 +1,33 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2017 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+#option ('divideByZero', 'nan');
+
+zero := nofold(0.0);
+
+//'INFINITY = '; INFINITY;
+'log10(0) = '; log(0.0); log(zero);
+
+'log(0) = '; ln(0.0); ln(zero);
+'log10(-1) = '; log(-1.0); log(zero - 1.0);
+'log(-1) = '; ln(-1.0); ln(zero - 1.0);
+'sqrt(-1) = '; sqrt(-1.0); sqrt(zero - 1.0);
+'1/0 = '; 1.0/0.0; 1.0 / zero;
+'fmod(1.0, 0.0) = '; 1.0 % 0.0; 1.0 % zero;
+'fmod(5.3, 2.0) = '; 5.3 % 2.0; 5.3 % (zero+2.0);
+'acos(2) = '; acos(2.0); acos(2.0 + zero);
+'asin(2) = '; asin(2.0); asin(2.0 + zero);

+ 33 - 0
testing/regress/ecl/zerodivide2.ecl

@@ -0,0 +1,33 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2017 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+#option ('divideByZero', 'zero');
+
+zero := nofold(0.0);
+
+//'INFINITY = '; INFINITY;
+'log10(0) = '; log(0.0); log(zero);
+
+'log(0) = '; ln(0.0); ln(zero);
+'log10(-1) = '; log(-1.0); log(zero - 1.0);
+'log(-1) = '; ln(-1.0); ln(zero - 1.0);
+'sqrt(-1) = '; sqrt(-1.0); sqrt(zero - 1.0);
+'1/0 = '; 1.0/0.0; 1.0 / zero;
+'fmod(1.0, 0.0) = '; 1.0 % 0.0; 1.0 % zero;
+'fmod(5.3, 2.0) = '; 5.3 % 2.0; 5.3 % (zero+2.0);
+'acos(2) = '; acos(2.0); acos(2.0 + zero);
+'asin(2) = '; asin(2.0); asin(2.0 + zero);

+ 27 - 0
testing/regress/ecl/zerodivide3.ecl

@@ -0,0 +1,27 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2017 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+#option ('divideByZero', 'fail');
+
+zero := nofold(0.0);
+
+catch(log(zero), 123);
+catch(ln(zero - 1.0), 124);
+catch(sqrt(zero - 1.0), 125);
+catch(1.0 / zero, 126);
+catch(acos(2.0 + zero), 127);
+catch(asin(2.0 + zero), 128);