Explorar el Código

HPCC-8352 Allow divide by zero action to be configured

Signed-off-by: Gavin Halliday <gavin.halliday@lexisnexis.com>
Gavin Halliday hace 12 años
padre
commit
b5753ac356

+ 31 - 78
common/deftype/defvalue.cpp

@@ -2462,11 +2462,6 @@ IValue * addValues(IValue * left, IValue * right)
                                     return res;                         
 
 
-IValue * addValues(unsigned num, IValue * * values)
-{
-    CALCULATE_AND_RETURN(addValues);
-}
-
 IValue * subtractValues(IValue * left, IValue * right)
 {
     IValue * ret;
@@ -2498,11 +2493,6 @@ IValue * subtractValues(IValue * left, IValue * right)
     return ret;
 }
 
-IValue * substractValues(unsigned num, IValue * * values)
-{
-    CALCULATE_AND_RETURN(subtractValues);
-}
-
 IValue * multiplyValues(IValue * left, IValue * right)
 {
     IValue * ret;
@@ -2534,14 +2524,18 @@ IValue * multiplyValues(IValue * left, IValue * right)
     return ret;
 }
 
-IValue * multiplyValues(unsigned num, IValue * * values)
+IValue * divideValues(IValue * left, IValue * right, DBZaction onZero)
 {
-    CALCULATE_AND_RETURN(multiplyValues);
-}
+    Owned<ITypeInfo> pnt = getPromotedMulDivType(left->queryType(), right->queryType());
 
-IValue * divideValues(IValue * left, IValue * right)
-{
-    ITypeInfo * pnt = getPromotedMulDivType(left->queryType(), right->queryType());
+    //All non zero values should return false when cast to boolean
+    if (!right->getBoolValue())
+    {
+        if (onZero == DBZnone)
+            return NULL;
+        if (onZero == DBZfail)
+            rtlFailDivideByZero();
+    }
 
     switch(pnt->getTypeCode())
     {
@@ -2559,24 +2553,22 @@ IValue * divideValues(IValue * left, IValue * right)
             else
                 res = (__int64)((unsigned __int64)lv / (unsigned __int64)rv);
         }
-        return createTruncIntValue(res, pnt);
+        return createTruncIntValue(res, pnt.getClear());
     }
     case type_real:
     {
         double lv = left->getRealValue();
         double rv = right->getRealValue();
-        double res = rv ? lv / rv : 0;
-        return createRealValue(res, pnt);
+        double res = rv ? lv / rv : 0.0;
+        return createRealValue(res, pnt.getClear());
     }
     case type_decimal:
     {
         BcdCriticalBlock bcdBlock;
         left->pushDecimalValue();
         right->pushDecimalValue();
-        DecDivide();   // returns 0 if divide by zero
-        IValue * ret = ((CDecimalTypeInfo*)pnt)->createValueFromStack();
-        pnt->Release();
-        return ret;
+        DecDivide(onZero);
+        return createDecimalValueFromStack(pnt);
     }
     default:
         assertThrow(false);
@@ -2584,16 +2576,19 @@ IValue * divideValues(IValue * left, IValue * right)
     }
 }
 
-IValue * divideValues(unsigned num, IValue * * values)
+IValue * modulusValues(IValue * left, IValue * right, DBZaction onZero)
 {
-    CALCULATE_AND_RETURN(divideValues);
-}
-
-IValue * modulusValues(IValue * left, IValue * right)
-{
-    IValue * ret;
-    ITypeInfo * pnt = getPromotedMulDivType(left->queryType(), right->queryType());
+    Owned<ITypeInfo> pnt = getPromotedMulDivType(left->queryType(), right->queryType());
     
+    //All non zero values should return false when cast to boolean
+    if (!right->getBoolValue())
+    {
+        if (onZero == DBZnone)
+            return NULL;
+        if (onZero == DBZfail)
+            rtlFailDivideByZero();
+    }
+
     switch(pnt->getTypeCode())
     {
     case type_int:
@@ -2610,33 +2605,26 @@ IValue * modulusValues(IValue * left, IValue * right)
             else
                 res = (__int64)((unsigned __int64)lv % (unsigned __int64)rv);
         }
-        return createTruncIntValue(res, pnt);
+        return createTruncIntValue(res, pnt.getClear());
     }
     case type_real:
     {
         double rv = right->getRealValue();
         double res = rv ? fmod(left->getRealValue(), rv) : 0;
-        return createRealValue(res, pnt);
+        return createRealValue(res, pnt.getClear());
     }
     case type_decimal:
     {
         BcdCriticalBlock bcdBlock;
         left->pushDecimalValue();
         right->pushDecimalValue();
-        DecModulus();
-        ret = ((CDecimalTypeInfo*)pnt)->createValueFromStack();
-        pnt->Release();
-        break;
+        DecModulus(onZero);
+        return createDecimalValueFromStack(pnt);
     }
     default:
         assertThrow(false);
+        return NULL;
     }
-    return ret;
-}
-
-IValue * modulusValues(unsigned num, IValue * * values)
-{
-    CALCULATE_AND_RETURN(modulusValues);
 }
 
 IValue * powerValues(IValue * left, IValue * right)
@@ -2669,11 +2657,6 @@ IValue * powerValues(IValue * left, IValue * right)
     return ret;
 }
 
-IValue * powerValues(unsigned num, IValue * * values)
-{
-    CALCULATE_AND_RETURN(powerValues);
-}
-
 IValue * negateValue(IValue * v)
 {
     switch(v->getTypeCode())
@@ -3105,11 +3088,6 @@ IValue * concatValues(IValue * left, IValue * right)
     }
 }
 
-IValue * concatValues(unsigned num, IValue * * values)
-{
-    CALCULATE_AND_RETURN(concatValues);
-}
-
 IValue * binaryAndValues(IValue * left, IValue * right)
 {
     IValue * ret;
@@ -3130,11 +3108,6 @@ IValue * binaryAndValues(IValue * left, IValue * right)
     return ret;
 }
 
-IValue * binaryAndValues(unsigned num, IValue * * values)
-{
-    CALCULATE_AND_RETURN(binaryAndValues);
-}
-
 IValue * binaryOrValues(IValue * left, IValue * right)
 {
     IValue * ret;
@@ -3155,11 +3128,6 @@ IValue * binaryOrValues(IValue * left, IValue * right)
     return ret;
 }
 
-IValue * binaryOrValues(unsigned num, IValue * * values)
-{
-    CALCULATE_AND_RETURN(binaryOrValues);
-}
-
 IValue * binaryXorValues(IValue * left, IValue * right)
 {
     IValue * ret;
@@ -3178,11 +3146,6 @@ IValue * binaryXorValues(IValue * left, IValue * right)
     return ret;
 }
 
-IValue * binaryXorValues(unsigned num, IValue * * values)
-{
-    CALCULATE_AND_RETURN(concatValues);
-}
-
 IValue * binaryNotValues(IValue * v)
 {
     switch(v->getTypeCode())
@@ -3206,21 +3169,11 @@ IValue * logicalAndValues(IValue * left, IValue * right)
     return createBoolValue(left->getBoolValue() && right->getBoolValue());
 }
 
-IValue * logicalAndValues(unsigned num, IValue * * values)
-{
-    CALCULATE_AND_RETURN(logicalAndValues);
-}
-
 IValue * logicalOrValues(IValue * left, IValue * right)
 {
     return createBoolValue(left->getBoolValue() || right->getBoolValue());
 }
 
-IValue * logicalOrValues(unsigned num, IValue * * values)
-{
-    CALCULATE_AND_RETURN(logicalOrValues);
-}
-
 int orderValues(IValue * left, IValue * right)
 {
     //The following line can be uncommented to check that the types are consistent everywhere

+ 2 - 2
common/deftype/defvalue.hpp

@@ -110,8 +110,8 @@ extern DEFTYPE_API IValue * createMinUIntValue(unsigned __int64 value);
 extern DEFTYPE_API IValue * addValues(IValue * left, IValue * right);
 extern DEFTYPE_API IValue * subtractValues(IValue * left, IValue * right);
 extern DEFTYPE_API IValue * multiplyValues(IValue * left, IValue * right);
-extern DEFTYPE_API IValue * divideValues(IValue * left, IValue * right);
-extern DEFTYPE_API IValue * modulusValues(IValue * left, IValue * right);
+extern DEFTYPE_API IValue * divideValues(IValue * left, IValue * right, DBZaction onZero);
+extern DEFTYPE_API IValue * modulusValues(IValue * left, IValue * right, DBZaction onZero);
 extern DEFTYPE_API IValue * powerValues(IValue * left, IValue * right);
 extern DEFTYPE_API IValue * shiftLeftValues(IValue * left, IValue * right);
 extern DEFTYPE_API IValue * shiftRightValues(IValue * left, IValue * right);

+ 16 - 2
ecl/hql/hqlfold.cpp

@@ -1749,9 +1749,23 @@ IHqlExpression * foldConstantOperator(IHqlExpression * expr, unsigned foldOption
             return applyBinaryFold(expr, multiplyValues);
         }
     case no_div:
-        return applyBinaryFold(expr, divideValues);
     case no_modulus:
-        return applyBinaryFold(expr, modulusValues);
+        {
+            IValue * leftValue = expr->queryChild(0)->queryValue();
+            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);
+                else
+                    res = modulusValues(leftValue, rightValue, onZero);
+                if (res)
+                    return createConstant(res);
+            }
+            return LINK(expr);
+        }
     case no_concat:
         return applyBinaryFold(expr, concatValues);
     case no_band:

+ 11 - 4
ecl/hql/hqlutil.cpp

@@ -2805,10 +2805,16 @@ IHqlExpression * getInverse(IHqlExpression * op)
 {
     node_operator opKind = op->getOperator();
     if (opKind == no_not)
-        return LINK(op->queryChild(0));
-    else if (opKind == no_constant)
+    {
+        IHqlExpression * arg0 = op->queryChild(0);
+        if (arg0->isBoolean())
+            return LINK(arg0);
+    }
+
+    if (opKind == no_constant)
         return createConstant(!op->queryValue()->getBoolValue());
-    else if (opKind == no_alias_scope)
+
+    if (opKind == no_alias_scope)
     {
         HqlExprArray args;
         args.append(*getInverse(op->queryChild(0)));
@@ -2830,7 +2836,8 @@ IHqlExpression * getInverse(IHqlExpression * op)
         return createBoolExpr(no_if, LINK(op->queryChild(0)), getInverse(op->queryChild(1)), getInverse(op->queryChild(2)));
     }
 
-    return createValue(no_not, makeBoolType(), LINK(op));
+    Owned<ITypeInfo> boolType = makeBoolType();
+    return createValue(no_not, LINK(boolType), ensureExprType(op, boolType));
 }
 
 IHqlExpression * getNormalizedCondition(IHqlExpression * expr)

+ 6 - 0
ecl/hqlcpp/hqlcatom.cpp

@@ -147,6 +147,7 @@ _ATOM createQStrRangeHighAtom;
 _ATOM createQuotedStringAtom;
 _ATOM createRangeLowAtom;
 _ATOM createRangeHighAtom;
+_ATOM createRealNullAtom;
 _ATOM createRowFromXmlAtom;
 _ATOM createStrRangeLowAtom;
 _ATOM createStrRangeHighAtom;
@@ -203,6 +204,7 @@ _ATOM DecSubAtom;
 _ATOM DecTruncateAtom;
 _ATOM DecTruncateAtAtom;
 _ATOM DecValidAtom;
+_ATOM DecValidTosAtom;
 _ATOM delayedAtom;
 _ATOM deleteFileAtom;
 _ATOM dependencyAtom;
@@ -272,6 +274,7 @@ _ATOM f2anAtom;
 _ATOM f2axAtom;
 _ATOM f2vnAtom;
 _ATOM f2vxAtom;
+_ATOM failDivideByZeroAtom;
 _ATOM _failIdAtom;
 _ATOM fileAtom;
 _ATOM fileExistsAtom;
@@ -869,6 +872,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM-1)
     MAKEATOM(createQuotedString);
     MAKEATOM(createRangeLow);
     MAKEATOM(createRangeHigh);
+    MAKEATOM(createRealNull);
     MAKEATOM(createRegex);
     MAKEATOM(createRowFromXml);
     MAKEATOM(createStrRangeLow);
@@ -925,6 +929,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM-1)
     MAKEATOM(DecTruncate);
     MAKEATOM(DecTruncateAt);
     MAKEATOM(DecValid);
+    MAKEATOM(DecValidTos);
     MAKEATOM(delayed);
     MAKEATOM(deleteFile);
     MAKEATOM(dependency);
@@ -994,6 +999,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM-1)
     f2axAtom = createAtom("_f2ax");
     MAKEATOM(f2vn);
     MAKEATOM(f2vx);
+    MAKEATOM(failDivideByZero);
     MAKEID(_fail);
     MAKEATOM(file);
     MAKEATOM(fileExists);

+ 3 - 0
ecl/hqlcpp/hqlcatom.hpp

@@ -147,6 +147,7 @@ extern _ATOM createQStrRangeHighAtom;
 extern _ATOM createQuotedStringAtom;
 extern _ATOM createRangeLowAtom;
 extern _ATOM createRangeHighAtom;
+extern _ATOM createRealNullAtom;
 extern _ATOM createRegexAtom;
 extern _ATOM createRowFromXmlAtom;
 extern _ATOM createStrRangeLowAtom;
@@ -203,6 +204,7 @@ extern _ATOM DecSubAtom;
 extern _ATOM DecTruncateAtom;
 extern _ATOM DecTruncateAtAtom;
 extern _ATOM DecValidAtom;
+extern _ATOM DecValidTosAtom;
 extern _ATOM delayedAtom;
 extern _ATOM deleteFileAtom;
 extern _ATOM dependencyAtom;
@@ -272,6 +274,7 @@ extern _ATOM f2anAtom;
 extern _ATOM f2axAtom;
 extern _ATOM f2vnAtom;
 extern _ATOM f2vxAtom;
+extern _ATOM failDivideByZeroAtom;
 extern _ATOM _failIdAtom;
 extern _ATOM fileAtom;
 extern _ATOM fileExistsAtom;

+ 84 - 25
ecl/hqlcpp/hqlcpp.cpp

@@ -1720,6 +1720,16 @@ void HqlCppTranslator::cacheOptions()
         }
     }
 
+    //Configure the divide by zero action
+    options.divideByZeroAction = DBZzero;
+    const char * dbz = wu()->getDebugValue("divideByZero",val).str();
+    if (strieq(val.str(), "0") || strieq(val.str(), "zero"))
+        options.divideByZeroAction = DBZzero;
+    else if (strieq(val.str(), "nan"))
+        options.divideByZeroAction = DBZnan;
+    else if (strieq(val.str(), "fail") || strieq(val.str(), "throw"))
+        options.divideByZeroAction = DBZfail;
+
     //The following cases handle options whose default values are dependent on other options.  
     //Or where one debug options sets more than one option
     options.hasResourceUseMpForDistribute = wu()->hasDebugValue("resourceUseMpForDistribute");
@@ -5145,11 +5155,23 @@ void HqlCppTranslator::doBuildExprArith(BuildCtx & ctx, IHqlExpression * expr, C
             _ATOM func;
             switch (expr->getOperator())
             {
-            case no_add: func = DecAddAtom; break;
-            case no_div: func = DecDivideAtom; break;
-            case no_modulus: func = DecModulusAtom; break;
-            case no_mul: func = DecMulAtom; break;
-            case no_sub: func = DecSubAtom; break;
+            case no_add:
+                func = DecAddAtom;
+                break;
+            case no_div:
+                func = DecDivideAtom;
+                args.append(*getSizetConstant(options.divideByZeroAction));
+                break;
+            case no_modulus:
+                func = DecModulusAtom;
+                args.append(*getSizetConstant(options.divideByZeroAction));
+                break;
+            case no_mul:
+                func = DecMulAtom;
+                break;
+            case no_sub:
+                func = DecSubAtom;
+                break;
             default: UNIMPLEMENTED;
             }
             callProcedure(ctx, func, args);
@@ -6980,7 +7002,10 @@ void HqlCppTranslator::doBuildExprDivide(BuildCtx & ctx, IHqlExpression * expr,
         int cmp = value->compare(zero);
 
         if (cmp == 0)
-            tgt.expr.setown(createConstant(zero.getClear()));
+        {
+            OwnedHqlExpr eZero = createConstant(LINK(zero));
+            doBuildDivideByZero(ctx, NULL, eZero, &tgt);
+        }
         else
             doBuildPureSubExpr(ctx, expr, tgt);
     }
@@ -6991,6 +7016,46 @@ void HqlCppTranslator::doBuildExprDivide(BuildCtx & ctx, IHqlExpression * expr,
 }
 
 
+
+void HqlCppTranslator::doBuildDivideByZero(BuildCtx & ctx, const CHqlBoundTarget * target, IHqlExpression * zero, CHqlBoundExpr * bound)
+{
+    //Always assign something to bound - even if it is replaced further down.
+    if (bound)
+        buildExpr(ctx, zero, *bound);
+
+    switch (options.divideByZeroAction)
+    {
+    case DBZzero:
+        if (target)
+            assignBound(ctx, *target, zero);
+        break;
+    case DBZfail:
+        {
+            HqlExprArray noArgs;
+            buildFunctionCall(ctx, failDivideByZeroAtom, noArgs);
+            break;
+        }
+    case DBZnan:
+        {
+            LinkedHqlExpr nan = zero;
+            if (zero->queryType()->getTypeCode() == type_real)
+            {
+                HqlExprArray noArgs;
+                nan.setown(bindFunctionCall(createRealNullAtom, noArgs));
+            }
+
+            //MORE:
+            if (target)
+                assignBound(ctx, *target, nan);
+            else
+                buildExpr(ctx, nan, *bound);
+            break;
+        }
+    default:
+        throwUnexpected();
+    }
+}
+
 void HqlCppTranslator::doBuildAssignDivide(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr)
 {
     if (expr->queryType()->getTypeCode() == type_decimal)
@@ -7017,7 +7082,7 @@ void HqlCppTranslator::doBuildAssignDivide(BuildCtx & ctx, const CHqlBoundTarget
         int cmp = value->compare(eZero->queryValue());
 
         if (cmp == 0)
-            assignBound(ctx, target, eZero);
+            doBuildDivideByZero(ctx, &target, eZero, NULL);
         else
             assignBound(ctx, target, pureExpr);
     }
@@ -7027,7 +7092,7 @@ void HqlCppTranslator::doBuildAssignDivide(BuildCtx & ctx, const CHqlBoundTarget
         IHqlStmt * stmt = subctx.addFilter(divisor);
         assignBound(subctx, target, pureExpr);
         subctx.selectElse(stmt);
-        assignBound(subctx, target, eZero);
+        doBuildDivideByZero(subctx, &target, eZero, NULL);
     }
 }
 
@@ -7544,22 +7609,8 @@ void HqlCppTranslator::doBuildAssignAll(BuildCtx & ctx, const CHqlBoundTarget &
 
 void HqlCppTranslator::doBuildExprNot(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt)
 {
-    ITypeInfo * type = expr->queryChild(0)->queryType();
-    switch (type->getTypeCode())
-    {
-    case type_decimal:
-        {
-            //MORE: Could leak decimal stack....  Is the last line correct?
-            HqlExprArray args;              
-            bindAndPush(ctx, expr->queryChild(0));
-            IHqlExpression * op = bindTranslatedFunctionCall(DecCompareNullAtom, args);
-            tgt.expr.setown(createValue(expr->getOperator(), LINK(boolType), op, getZero()));
-        }
-        break;
-    default:
-        doBuildPureSubExpr(ctx, expr, tgt);
-        break;
-    }
+    assertex(expr->queryChild(0)->isBoolean());
+    doBuildPureSubExpr(ctx, expr, tgt);
 }
 
 
@@ -9774,10 +9825,18 @@ void HqlCppTranslator::doBuildExprIsValid(BuildCtx & ctx, IHqlExpression * expr,
 
     CHqlBoundExpr bound;
     buildExpr(ctx, value, bound);
+
+    type_t tc = type->getTypeCode();
+    if ((tc == type_decimal) && (bound.expr->getOperator() == no_decimalstack))
+    {
+        tgt.expr.setown(bindTranslatedFunctionCall(DecValidTosAtom, args));
+        return;
+    }
+
     ensureHasAddress(ctx, bound);
 
     OwnedHqlExpr address = getPointer(bound.expr);
-    switch (type->getTypeCode())
+    switch (tc)
     {
     case type_decimal:
         args.append(*createConstant(type->isSigned()));

+ 2 - 0
ecl/hqlcpp/hqlcpp.ipp

@@ -555,6 +555,7 @@ struct HqlCppOptions
     unsigned            complexClassesThreshold;
     unsigned            complexClassesActivityFilter;
     CompilerType        targetCompiler;
+    DBZaction           divideByZeroAction;
     bool                peephole;
     bool                foldConstantCast;
     bool                optimizeBoolReturn;
@@ -1129,6 +1130,7 @@ public:
 
     void buildExprAssignViaType(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr, ITypeInfo * type);
     void buildExprAssignViaString(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr, unsigned len);
+    void doBuildDivideByZero(BuildCtx & ctx, const CHqlBoundTarget * target, IHqlExpression * zero, CHqlBoundExpr * bound);
 
     void doBuildAssignAddSets(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * value);
     void doBuildAssignAggregate(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * expr);

+ 6 - 3
ecl/hqlcpp/hqlcppsys.ecl

@@ -313,8 +313,8 @@ const char * cppSystemText[]  = {
     "   integer4 DecCompareNull() : bcd,library='nbcd',entrypoint='DecCompareNull';",
     "   integer4 DecDistinct() :    bcd,library='nbcd',entrypoint='DecDistinct';",
     "   integer4 DecDistinctR() :   bcd,library='nbcd',entrypoint='DecDistinctR';",
-    "   DecDivide() :   bcd,library='nbcd',entrypoint='DecDivide';",
-    "   DecModulus() :  bcd,library='nbcd',entrypoint='DecModulus';",
+    "   DecDivide(unsigned1 dbz) :   bcd,library='nbcd',entrypoint='DecDivide';",
+    "   DecModulus(unsigned1 dbz) :  bcd,library='nbcd',entrypoint='DecModulus';",
     "   DecSub() :  bcd,library='nbcd',entrypoint='DecSub';",
     "   DecSubR() : bcd,library='nbcd',entrypoint='DecSubR';",
     "   DecNegate() :   bcd,library='nbcd',entrypoint='DecNegate';",
@@ -339,7 +339,7 @@ const char * cppSystemText[]  = {
     "   DecRoundTo(unsigned4 places) :  bcd,library='nbcd',entrypoint='DecRoundTo';",
     "   DecDup() :  bcd,library='nbcd',entrypoint='DecDup';",
     "   DecMul() :  bcd,library='nbcd',entrypoint='DecMul';",
-    "   DecDivideR() :  bcd,library='nbcd',entrypoint='DecDivideR';",
+    "   DecDivideR(unsigned1 dbz) :  bcd,library='nbcd',entrypoint='DecDivideR';",
     "   DecLongPower(integer4 pow) :    bcd,library='nbcd',entrypoint='DecLongPower';",
     "   DecPushReal(real8 d) :  bcd,library='nbcd',entrypoint='DecPushReal';",
     "   DecPushUtf8(utf8 d) :   bcd,library='nbcd',entrypoint='rtlDecPushUtf8';",
@@ -354,6 +354,7 @@ const char * cppSystemText[]  = {
     "   DecLock() : bcd,library='nbcd',entrypoint='DecLock';",
     "   DecUnlock() :   bcd,library='nbcd',entrypoint='DecUnlock';",
     "   boolean DecValid(boolean isSigned, const data src) : bcd,pure,library='nbcd',entrypoint='DecValid';",
+    "   boolean DecValidTos() : bcd,pure,library='nbcd',entrypoint='DecValidTos';",
 
     "   integer8 _ROUND(real8 arg) :    eclrtl,pure,library='eclrtl',entrypoint='rtlRound';",
     "   real8 roundTo(real8 arg, integer4 _places) : eclrtl,pure,library='eclrtl',entrypoint='rtlRoundTo';",
@@ -362,6 +363,7 @@ const char * cppSystemText[]  = {
     "   unsigned4 rtlRandom() : eclrtl,library='eclrtl',entrypoint='rtlRandom';",
     "   _fail(integer4 code, const varstring msg) : eclrtl,library='eclrtl',entrypoint='rtlFail';",
     "   sysFail(integer4 code, const varstring msg) :   eclrtl,library='eclrtl',entrypoint='rtlSysFail';",
+    "   failDivideByZero() : eclrtl,library='eclrtl',entrypoint='rtlFailDivideByZero';",
 
     "   unsigned4 crcData(const data src, unsigned4 initval) :  eclrtl,pure,library='eclrtl',entrypoint='rtlCrcData';",
     "   unsigned4 crcUnicode(const unicode src, unsigned4 initval) :    eclrtl,pure,library='eclrtl',entrypoint='rtlCrcUnicode';",
@@ -401,6 +403,7 @@ const char * cppSystemText[]  = {
     "   data16 hashMd5Finish(data _state) : eclrtl,entrypoint='rtlHashMd5Finish';",
 
     "   boolean validReal(const data src) : eclrtl,pure,library='eclrtl',entrypoint='rtlIsValidReal';",
+    "   real8 createRealNull() : eclrtl,pure,library='eclrtl',entrypoint='rtlCreateRealNull';",
 
     "   deserializeRaw(data field, boolean o) : eclrtl,include='eclrtl.hpp',library='eclrtl',entrypoint='deserializeRaw';",
     "   data deserializeDataX(boolean o) :  eclrtl,include='eclrtl.hpp',library='eclrtl',entrypoint='deserializeDataX';",

+ 2 - 0
ecl/regress/indexread32.ecl

@@ -16,6 +16,8 @@
 ############################################################################## */
 
 #option ('globalFold', false);
+#option ('checkThorRestrictions', false);
+
 d := dataset('~local::rkc::person', { string15 name, unsigned8 filepos{virtual(fileposition)} }, flat);
 
 i := stepped(index(d, { name, filepos }, {},'\\home\\person.name_first.key', hint(thisIsAHint(5))), filepos,hint(anotherHint));

+ 43 - 0
ecl/regress/issue8352a.ecl

@@ -0,0 +1,43 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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.
+############################################################################## */
+
+//Test division by zero - default action to return 0
+#option ('divideByZero', '0'); 
+
+unsigned cintZero := 0;
+real crealZero := 0.0;
+decimal10_2 cdecZero := 0.0D;
+
+unsigned intZero := 0 : stored('intZero');
+real realZero := 0.0 : stored('realZero');
+decimal10_2 decZero := 0.0D : stored('decZero');
+
+output(100 DIV 1);
+output(100.0 / 1.0);
+output(100.1D / 1.0D);
+
+output(100 DIV cintZero);
+output(100.0 / crealZero);
+output(100.1D / cdecZero);
+            
+output(5 * (101 DIV cintZero));
+output(5 * (101.0 / crealZero));
+output(5D * (101.1D / cdecZero));
+            
+output(100 DIV intZero);
+output(100.0 / realZero);
+output(100.1D / decZero);

+ 43 - 0
ecl/regress/issue8352b.ecl

@@ -0,0 +1,43 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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.
+############################################################################## */
+
+//Test division by zero - default action to return 0
+#option ('divideByZero', 'fail'); 
+
+unsigned cintZero := 0;
+real crealZero := 0.0;
+decimal10_2 cdecZero := 0.0D;
+
+unsigned intZero := 0 : stored('intZero');
+real realZero := 0.0 : stored('realZero');
+decimal10_2 decZero := 0.0D : stored('decZero');
+
+output(100 DIV 1);
+output(100.0 / 1.0);
+output(100.1D / 1.0D);
+
+output(100 DIV cintZero);
+output(100.0 / crealZero);
+output(100.1D / cdecZero);
+            
+output(5 * (101 DIV cintZero));
+output(5 * (101.0 / crealZero));
+output(5D * (101.1D / cdecZero));
+            
+output(100 DIV intZero);
+output(100.0 / realZero);
+output(100.1D / decZero);

+ 30 - 0
ecl/regress/issue8352c1.ecl

@@ -0,0 +1,30 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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.
+############################################################################## */
+
+//Test division by zero - fail instead of returning 0
+#option ('divideByZero', 'fail'); 
+
+unsigned cintZero := 0;
+real crealZero := 0.0;
+decimal10_2 cdecZero := 0.0D;
+
+//The constant folding in the pre-processor defaults to throwing an error
+#IF (100 DIV cintZero = 0)
+OUTPUT('success');
+#else
+OUTPUT('failure');
+#END

+ 30 - 0
ecl/regress/issue8352c2.ecl

@@ -0,0 +1,30 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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.
+############################################################################## */
+
+//Test division by zero - fail instead of returning 0
+#option ('divideByZero', 'fail'); 
+
+unsigned cintZero := 0;
+real crealZero := 0.0;
+decimal10_2 cdecZero := 0.0D;
+
+//The constant folding in the pre-processor defaults to throwing an error
+#IF (100.0 / crealZero = 0)
+OUTPUT('success');
+#else
+OUTPUT('failure');
+#END

+ 30 - 0
ecl/regress/issue8352c3.ecl

@@ -0,0 +1,30 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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.
+############################################################################## */
+
+//Test division by zero - fail instead of returning 0
+#option ('divideByZero', 'fail'); 
+
+unsigned cintZero := 0;
+real crealZero := 0.0;
+decimal10_2 cdecZero := 0.0D;
+
+//The constant folding in the pre-processor defaults to throwing an error
+#IF (100.1D / cdecZero = 0)
+OUTPUT('success');
+#else
+OUTPUT('failure');
+#END

+ 29 - 0
ecl/regress/issue8352c4.ecl

@@ -0,0 +1,29 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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', '0'); 
+
+unsigned cintZero := 0;
+real crealZero := 0.0;
+decimal10_2 cdecZero := 0.0D;
+
+//The constant folding in the pre-processor defaults to throwing an error
+#IF (100.0 / crealZero = 0)
+OUTPUT('success');
+#else
+OUTPUT('failure');
+#END

+ 78 - 0
ecl/regress/issue8352d.ecl

@@ -0,0 +1,78 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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.
+############################################################################## */
+
+//Test division by zero - fail instead of returning 0
+#option ('divideByZero', 'nan'); 
+
+unsigned cintZero := 0;
+real crealZero := 0.0;
+decimal10_2 cdecZero := 0.0D;
+
+unsigned intZero := 0 : stored('intZero');
+real realZero := 0.0 : stored('realZero');
+decimal10_2 decZero := 0.0D : stored('decZero');
+
+output(NOT ISNULL(100 DIV 1));
+output(NOT ISNULL(100.0 / 1.0));
+output(NOT ISNULL(100.1D / 1.0D));
+
+output('Constant Divide:');
+output(ISNULL(100 DIV cintZero));
+output(ISNULL(100.0 / crealZero));
+output(ISNULL(100.1D / cdecZero));
+            
+output('Constant Divide Multiply:');
+output(ISNULL(5 * (101 DIV cintZero)));
+output(ISNULL(5 * (101.0 / crealZero)));
+output(ISNULL(5D * (101.1D / cdecZero)));
+            
+output('Runtime Divide:');
+output(ISNULL(100 DIV intZero));
+output(ISNULL(100.0 / realZero));
+output(ISNULL(100.1D / decZero));
+
+//--- check modulus
+output('Modulus:');
+
+output(NOT ISNULL(100 % 1));
+output(NOT ISNULL(100.0 % 1.0));
+output(NOT ISNULL(100.1D % 1.0D));
+
+output('Constant Modulus:');
+output(ISNULL(100 % cintZero));
+output(ISNULL(100.0 % crealZero));
+output(ISNULL(100.1D % cdecZero));
+            
+output('Constant Modulus Multiply:');
+output(ISNULL(5 * (101 % cintZero)));
+output(ISNULL(5 * (101.0 % crealZero)));
+output(ISNULL(5D * (101.1D % cdecZero)));
+            
+output('Runtime Modulus:');
+output(ISNULL(100 % intZero));
+output(ISNULL(100.0 % realZero));
+output(ISNULL(100.1D % decZero));
+
+output('Miscellaneous');
+output(100.0 / crealZero);
+output(100.0 / realZero);
+output(1.0e300 * nofold(1.0e300));
+output(-1.0e300 * nofold(1.0e300));
+
+output((unsigned)(100.0 / crealZero));
+output((unsigned)(100.0 / realZero));
+

+ 25 - 0
ecl/regress/issue8352e1.ecl

@@ -0,0 +1,25 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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.
+############################################################################## */
+
+//Test division by zero - fail instead of returning 0
+#option ('divideByZero', 'fail'); 
+
+unsigned intZero := 0 : stored('intZero');
+real realZero := 0.0 : stored('realZero');
+decimal10_2 decZero := 0.0D : stored('decZero');
+
+output(100 DIV intZero);

+ 25 - 0
ecl/regress/issue8352e2.ecl

@@ -0,0 +1,25 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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.
+############################################################################## */
+
+//Test division by zero - fail instead of returning 0
+#option ('divideByZero', 'fail'); 
+
+unsigned intZero := 0 : stored('intZero');
+real realZero := 0.0 : stored('realZero');
+decimal10_2 decZero := 0.0D : stored('decZero');
+
+output(100.0 / realZero);

+ 25 - 0
ecl/regress/issue8352e3.ecl

@@ -0,0 +1,25 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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.
+############################################################################## */
+
+//Test division by zero - fail instead of returning 0
+#option ('divideByZero', 'fail'); 
+
+unsigned intZero := 0 : stored('intZero');
+real realZero := 0.0 : stored('realZero');
+decimal10_2 decZero := 0.0D : stored('decZero');
+
+output(100.1D / decZero);

+ 25 - 0
ecl/regress/issue8352e4.ecl

@@ -0,0 +1,25 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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.
+############################################################################## */
+
+//Test division by zero - fail instead of returning 0
+#option ('divideByZero', 'fail'); 
+
+unsigned intZero := 0 : stored('intZero');
+real realZero := 0.0 : stored('realZero');
+decimal10_2 decZero := 0.0D : stored('decZero');
+
+output(100.1D % decZero);

+ 22 - 0
ecl/regress/issue8352f.ecl

@@ -0,0 +1,22 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2013 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.
+############################################################################## */
+
+decimal10_2 myDec := 10.01D : stored('mydec');
+
+output((boolean) NOFOLD(100.1D));
+output(NOT NOFOLD(100.1D)); // Strangely this is done as a integer bitwise not
+output(NOT myDec);          // Strangely this is done as a integer bitwise not

+ 19 - 0
rtl/eclrtl/eclrtl.cpp

@@ -3731,6 +3731,12 @@ void rtlFailOnAssert()
 {
     throw MakeStringException(MSGAUD_user, -1, "Abort execution");
 }
+
+void rtlFailDivideByZero()
+{
+    throw MakeStringException(MSGAUD_user, -1, "Division by zero");
+}
+
 //---------------------------------------------------------------------------
 
 void deserializeRaw(unsigned recordSize, void *record, MemoryBuffer &in)
@@ -4068,6 +4074,19 @@ ECLRTL_API bool rtlIsValidReal(unsigned size, const void * data)
     return true;
 }
 
+double rtlCreateRealNull()
+{
+    union
+    {
+        byte data[8];
+        double r;
+    } u;
+    //Use a non-signaling NaN
+    memcpy(u.data, "\x01\x00\x00\x00\x00\x00\xF0\x7f", 8);
+    return u.r;
+}
+
+
 void rtlUnicodeToUnicode(size32_t outlen, UChar * out, size32_t inlen, UChar const *in)
 {
     if(inlen>outlen) inlen = outlen;

+ 2 - 0
rtl/eclrtl/eclrtl.hpp

@@ -380,6 +380,7 @@ ECLRTL_API void rtlFail(int code, const char *msg);
 ECLRTL_API void rtlSysFail(int code, const char *msg);
 ECLRTL_API void rtlFailUnexpected();
 ECLRTL_API void rtlFailOnAssert();
+ECLRTL_API void rtlFailDivideByZero();
 
 ECLRTL_API void rtlReportFieldOverflow(unsigned size, unsigned max, const char * name);
 ECLRTL_API void rtlReportRowOverflow(unsigned size, unsigned max);
@@ -579,6 +580,7 @@ ECLRTL_API double rtlACos(double x);
 ECLRTL_API double rtlASin(double x);
 
 ECLRTL_API bool rtlIsValidReal(unsigned size, const void * data);
+ECLRTL_API double rtlCreateRealNull();
 
 ECLRTL_API unsigned rtlQStrLength(unsigned size);
 ECLRTL_API unsigned rtlQStrSize(unsigned length);

+ 4 - 3
rtl/nbcd/bcd.hpp

@@ -39,8 +39,8 @@ nbcd_decl void  _fastcall  DecAdd( void );         // add top two values on stac
 nbcd_decl int   _fastcall  DecCompareNull( void ); // compare top of stack to NULL, returns result, takes value of stack
 nbcd_decl int   _fastcall  DecDistinct( void );    // compares second to top value on stack, removes them and returns result
 nbcd_decl int   _fastcall  DecDistinctR( void );   // compares top to second value on stack, removes them and returns result
-nbcd_decl void  _fastcall  DecDivide( void );      // divide second by top value on stack and replace with result
-nbcd_decl void  _fastcall  DecDivideR( void );     // divide top by second value on stack and replace with result
+nbcd_decl void  _fastcall  DecDivide(byte dbz);      // divide second by top value on stack and replace with result
+nbcd_decl void  _fastcall  DecDivideR(byte dbz);     // divide top by second value on stack and replace with result
 nbcd_decl void  _fastcall  DecDup( void );         // duplicate value on top of decimal stack
 nbcd_decl void  _fastcall  DecSetPrecision(unsigned char declen, unsigned char prec); // changes length and precision of top value on stack
 nbcd_decl void  _fastcall  DecSub( void );         // substract top from second value on stack and replace with result
@@ -48,7 +48,7 @@ nbcd_decl void  _fastcall  DecSubR( void );        // substract second from top
 nbcd_decl void  _fastcall  DecInfo(unsigned & digits, unsigned & prec); // returns the digits and precision of top value on stack
 nbcd_decl void  _fastcall  DecClipInfo(unsigned & digits, unsigned & prec);
 nbcd_decl void  _fastcall  DecLongPower(long pow);   // calculates top of stack to the power of long and replaces with result
-nbcd_decl void  _fastcall  DecModulus( void );   // modulus second by top value on stack and replace with result
+nbcd_decl void  _fastcall  DecModulus(byte dbz);   // modulus second by top value on stack and replace with result
 nbcd_decl void  _fastcall  DecMul( void );         // multiply values top and second on the stack and replace with result
 nbcd_decl void  _fastcall  DecNegate( void );      // negate value on top of the decimal stack
 nbcd_decl void  _fastcall  DecPopCString (unsigned, char *); // Pops decimal accumulator into CString
@@ -83,6 +83,7 @@ nbcd_decl void  _fastcall  DecUlongPower(unsigned long pow); // calculates top o
 nbcd_decl void  _fastcall  DecLock();
 nbcd_decl void  _fastcall  DecUnlock();
 nbcd_decl bool  _fastcall  DecValid(bool isSigned, unsigned digits, const void * data);
+nbcd_decl bool  _fastcall  DecValidTos();
 nbcd_decl bool  _fastcall  Dec2Bool(size32_t bytes, const void * data);
 nbcd_decl bool  _fastcall  UDec2Bool(size32_t bytes, const void * data);
 

+ 22 - 6
rtl/nbcd/nbcd.cpp

@@ -18,6 +18,7 @@
 #include "platform.h"
 #include "nbcd.hpp"
 #include "jlib.hpp"
+#include "jexcept.hpp"
 
 #ifdef _WIN32
  #define NOMEMCPY volatile          // stop VC++ doing a stupid optimization
@@ -141,7 +142,7 @@ int Decimal::compare(const Decimal & other) const
 }
 
 
-Decimal & Decimal::divide(const Decimal & other)
+Decimal & Decimal::divide(const Decimal & other, DBZaction dbz)
 {
     //NB: Round towards zero
     int lo1, hi1, lo2, hi2;
@@ -159,8 +160,7 @@ Decimal & Decimal::divide(const Decimal & other)
     }
     if (hi2 < lo2)
     {
-        //divide by zero
-        setZero();
+        setDivideByZero(dbz);
         return *this;
     }
 
@@ -250,10 +250,10 @@ void Decimal::extendRange(byte oLsb, byte oMsb)
 }
 
 
-Decimal & Decimal::modulus(const Decimal & other)
+Decimal & Decimal::modulus(const Decimal & other, DBZaction dbz)
 {
     Decimal left(*this);
-    left.divide(other).truncate(0).multiply(other);
+    left.divide(other, dbz).truncate(0).multiply(other);
     return subtract(left);
 }
 
@@ -342,7 +342,7 @@ Decimal & Decimal::power(int value)
     //This probably gives slightly more expected results, but both suffer from rounding errors.
     Decimal reciprocal;
     reciprocal.setInt(1);
-    reciprocal.divide(*this);
+    reciprocal.divide(*this, DBZzero);  // MORE: This should probably be passed in
     set(reciprocal);
     doPower((unsigned)-value);
     return *this;
@@ -1157,6 +1157,22 @@ void Decimal::setZero()
     digits[zeroDigit] = 0;
 }
 
+
+void Decimal::setDivideByZero(DBZaction dbz)
+{
+    switch (dbz)
+    {
+    case DBZfail:
+        //No dependency on eclrtl, so throw a jlib exception directly
+        throw MakeStringException(MSGAUD_user, -1, "Division by zero");
+    case DBZnan:
+    case DBZzero:
+        setZero();
+        break;
+    default:
+        throwUnexpected();
+    }
+}
 //---------------------------------------------------------------------------
 
 

+ 7 - 4
rtl/nbcd/nbcd.hpp

@@ -56,8 +56,8 @@ public:
     Decimal & add(const Decimal & other);
     int compareNull() const;
     int compare(const Decimal & other) const;
-    Decimal & divide(const Decimal & other);
-    Decimal & modulus(const Decimal & other);
+    Decimal & divide(const Decimal & other, DBZaction dbz);
+    Decimal & modulus(const Decimal & other, DBZaction dbz);
     Decimal & multiply(const Decimal & other);
     Decimal & negate();
     Decimal & power(int value);
@@ -84,6 +84,8 @@ public:
     void getClipPrecision(unsigned & digits, unsigned & precision);
     void getPrecision(unsigned & digits, unsigned & precison);
 
+    bool isValid() const { return true; } // MORE: Should extend to support Nans
+
     void set(const Decimal & value);
     void setCString(const char * buffer);
     void setDecimal(byte length, byte precision, const void * buffer);
@@ -126,6 +128,7 @@ protected:
     void extendRange(byte oLsb, byte oMsb);
     void extendRange(const Decimal & other)     { extendRange(other.lsb, other.msb); }
     void overflow();
+    void setDivideByZero(DBZaction dbz);
 
 private:
     Decimal & incLSD();
@@ -171,8 +174,8 @@ protected:
 inline Decimal operator + (const Decimal & left, const Decimal & right) { return Decimal(left).add(right); }
 inline Decimal operator - (const Decimal & left, const Decimal & right) { return Decimal(left).subtract(right); }
 inline Decimal operator * (const Decimal & left, const Decimal & right) { return Decimal(left).multiply(right); }
-inline Decimal operator / (const Decimal & left, const Decimal & right) { return Decimal(left).divide(right); }
-inline Decimal operator % (const Decimal & left, const Decimal & right) { return Decimal(left).modulus(right); }
+inline Decimal operator / (const Decimal & left, const Decimal & right) { return Decimal(left).divide(right, DBZzero); }
+inline Decimal operator % (const Decimal & left, const Decimal & right) { return Decimal(left).modulus(right, DBZzero); }
 inline bool operator == (const Decimal & left, const Decimal & right) { return left.compare(right) == 0; }
 inline bool operator != (const Decimal & left, const Decimal & right) { return left.compare(right) != 0; }
 inline bool operator >= (const Decimal & left, const Decimal & right) { return left.compare(right) >= 0; }

+ 11 - 6
rtl/nbcd/nbcds.cpp

@@ -67,16 +67,16 @@ nbcd_decl int _fastcall  DecDistinctR()
     return stack[curStack+1].compare(stack[curStack]);
 }
 
-nbcd_decl void _fastcall  DecDivide()
+nbcd_decl void _fastcall  DecDivide(byte dbz)
 {
     curStack--;
-    stack[curStack-1].divide(stack[curStack]);
+    stack[curStack-1].divide(stack[curStack], (DBZaction)dbz);
 }
 
-nbcd_decl void _fastcall  DecDivideR()
+nbcd_decl void _fastcall  DecDivideR(byte dbz)
 {
     DecSwap();
-    DecDivide();
+    DecDivide(dbz);
 }
 
 nbcd_decl void _fastcall  DecDup()
@@ -122,10 +122,10 @@ nbcd_decl void _fastcall  DecUlongPower(unsigned long pow)
     stack[curStack-1].power((unsigned)pow);
 }
 
-nbcd_decl void  _fastcall  DecModulus()
+nbcd_decl void  _fastcall  DecModulus(byte dbz)
 {
     curStack--;
-    stack[curStack-1].modulus(stack[curStack]);
+    stack[curStack-1].modulus(stack[curStack], (DBZaction)dbz);
 }
 
 nbcd_decl void _fastcall  DecMul()
@@ -274,6 +274,11 @@ nbcd_decl bool _fastcall  DecValid(bool isSigned, unsigned digits, const void *
     return decValid(isSigned, digits, data);
 }
 
+nbcd_decl bool _fastcall  DecValidTos()
+{
+    return stack[--curStack].isValid();
+}
+
 nbcd_decl bool _fastcall  Dec2Bool(size32_t bytes, const void * data)
 {
     return dec2Bool(bytes, data);

+ 4 - 4
rtl/nbcd/nbcdtest.cpp

@@ -108,12 +108,12 @@ protected:
         char temp[80];
         Decimal a = left;
         Decimal b = right;
-        a.divide(b);
+        a.divide(b, DBZzero);
         a.getCString(sizeof(temp), temp);
         cppunit_assert(strcmp(expected, temp) == 0, "ERROR: testDivide/getCString: expected '%s', got '%s'", expected, temp);
         DecPushCString(left);
         DecPushCString(right);
-        DecDivide();
+        DecDivide(DBZzero);
         DecPopCString(sizeof(temp),temp);
         cppunit_assert(strcmp(expected, temp) == 0, "ERROR: testDivide/DecDivide: expected '%s', got '%s'", expected, temp);
     }
@@ -138,7 +138,7 @@ protected:
         char temp[80];
         Decimal a = left;
         Decimal b = right;
-        a.modulus(b);
+        a.modulus(b, DBZzero);
         a.getCString(sizeof(temp), temp);
         cppunit_assert(strcmp(expected, temp) == 0, "ERROR: testModulus: expected '%s', got '%s'", expected, temp);
     }
@@ -552,7 +552,7 @@ protected:
                 }
 
                 sofar1.multiply(value);
-                sofar2.divide(value);
+                sofar2.divide(value, DBZzero);
             }
             cppunit_assert(success, "ERROR: testBcdPower: one or more errors detected above.");
         }

+ 2 - 0
system/include/platform.h

@@ -431,6 +431,8 @@ typedef int socklen_t;
 // **** END   OF UNIX SPECIFIC SECTION ****
 #endif
 
+enum DBZaction { DBZnone, DBZzero, DBZnan, DBZfail }; // Different actions on divide by zero
+
 #define FLOAT_SIG_DIGITS    7
 #define DOUBLE_SIG_DIGITS   16
 

+ 4 - 0
system/jlib/jstring.cpp

@@ -218,6 +218,8 @@ StringBuffer & StringBuffer::append(double value)
         case '.':
         case 'E':
         case 'e':
+        case 'N': // Not a number/infinity
+        case 'n':
             return *this;
         }
         len++;
@@ -236,6 +238,8 @@ StringBuffer & StringBuffer::append(float value)
         case '.':
         case 'E':
         case 'e':
+        case 'N': // Not a number/infinity
+        case 'n':
             return *this;
         }
         len++;