# Clarion Rounding Values (similar to: Math.Ceiling Method)

Hello -

What would be an ideal equivalent in Clarion to round value up/down similar to these .NET Math.Ceiling Method and Math.Floor Method

Example:

Math.Ceiling Method (System) | Microsoft Docs

decimal values = {7.03m, 7.64m, 0.12m, -0.12m, -7.1m, -7.6m};
Console.WriteLine(" Value Ceiling Floor\n");
foreach (decimal value in values)
Console.WriteLine("{0,7} {1,16} {2,14}",
value, Math.Ceiling(value), Math.Floor(value));
// The example displays the following output to the console:
// Value Ceiling Floor
//
// 7.03 8 7
// 7.64 8 7
// 0.12 1 0
// -0.12 0 -1
// -7.1 -7 -8
// -7.6 -7 -8

I think this works, but please test:

`````` ceiling = ROUND(value + 0.5,1)
floor = ROUND(value - 0.5,1)``````
1 Like

Ceiling(Decimal)

Returns the smallest integral value that is greater than or equal to the specified decimal number.

I think Ceiling(7.0) returns 7 as “equal to the specified decimal number”

While Round(7+0.5,1) returns 8.

So maybe + .4999 or use an if like Choose(x=int(x), x, Int(x)+1) but double check how that will work with negatives.

4 Likes

I had written this function not long ago and was checking the results when i came acros this question, here you go:

Usage:

``````  Round (x, 0.5, 1) ! Rounds up to halves
Round (x, 1.0, -1) ! Rounds down to wholes
Round (x, 0.01, 0) ! Standard rounding to cents``````
``````RoundAny             PROCEDURE  (REAL pExpression,REAL pOrder,REAL pDirection)
! ELA: Extension to standard round to support rounding to any number of digits with any value
CODE
IF    pDirection < 0 THEN ! Round Down
RETURN INT(pExpression / pOrder) * pOrder
ELSIF pDirection > 0 THEN ! Round Up
RETURN INT((pExpression + pOrder - 1.0e-100) / pOrder) * pOrder
ELSE ! Round up/down
RETURN ROUND(pExpression / pOrder, 1) * pOrder
END``````
1 Like

Thank you Elankreijer.

RoundAny function does not return value for CEILING(7.0). As Carl had pointed earlier, Ceiling(7.0) returns 7 as “equal to the specified decimal number”.

x REAL
x= 7.00
RoundAny (x, 1.0, 1)

Returns 8.00, correct value is 7.00

I think Carl’s suggestion of checking if x = int(x) before doing any rounding is a good idea.

The following matches all of the expected values from Microsoft’s documentation

note: I normally use OutputDebugString instead of MESSAGE
but I wanted to make this extra easy for everyone to run for themselves

``````  PROGRAM

MAP
Ceiling(REAL value),LONG
Floor(REAL value),LONG
Test(REAL value, LONG expectedCeiling, LONG expectedFloor),BOOL
END

Pass LONG(0)
Fail LONG(0)
CODE

IF Test( 7.03,  8,  7) THEN Pass += 1 ELSE Fail += 1 END
IF Test( 7.64,  8,  7) THEN Pass += 1 ELSE Fail += 1 END
IF Test( 0.12,  1,  0) THEN Pass += 1 ELSE Fail += 1 END
IF Test(-0.12,  0, -1) THEN Pass += 1 ELSE Fail += 1 END
IF Test(-7.1 , -7, -8) THEN Pass += 1 ELSE Fail += 1 END
IF Test(-7.6 , -7, -8) THEN Pass += 1 ELSE Fail += 1 END
IF Test(-7.0 , -7, -7) THEN Pass += 1 ELSE Fail += 1 END
IF Test( 7.0 ,  7,  7) THEN Pass += 1 ELSE Fail += 1 END

MESSAGE('Passed['& Pass &']|Failed['& Fail &']','Tests complete')

Ceiling PROCEDURE(REAL value)
CODE
IF Value < 0 OR INT(Value) = Value
Answer =     Value  ! <-- rely on automatic type conversion to do the INT() for us for < 0
ELSE Answer = INT(Value) + 1
END

Floor  PROCEDURE(REAL value)
! revised implementation by @RexCalifornia
CODE
IF INT(Value) = Value THEN RETURN Value
ELSIF  Value < 0      THEN RETURN Value - 1   ! really INT(Value) - 1
ELSE RETURN Value       ! really INT(Value)
END
! relying on automatic data type conversion to do the INT() for us

Test  PROCEDURE(REAL value, LONG expectedCeiling, LONG expectedFloor)
Passed BOOL(TRUE)
CODE
IF Ceiling(Value) <> expectedCeiling |
OR Floor(Value) <> expectedFloor   |
THEN
MESSAGE(    'Value['& Value          &']' |
& '|Ceiling: Actual['& Ceiling(Value) &'] Expected['& expectedCeiling &']' |
&  '|Floor : Actual['&   Floor(Value) &'] Expected['& expectedFloor   &']' |
, 'Test Failed'|
)
Passed = FALSE
END
RETURN Passed``````
1 Like

Thank you, Mark, this is very helpful!

Just noticed that Floor Procedure does not return correct value for -7.00, correct value is -7.00

MS Excel has similar function and that can be used for verifying.

``````Floor  PROCEDURE(REAL value)
CODE
IF INT(Value) = Value
RETURN Value
ELSIF  Value < 0
RETURN Value - 1
ELSE
RETURN Value
END
`````` Hi i think that is he used a very small number, which cannot be represented with Real variable limitations, after subtracted from the previous expression. Particulary as the mantisa is only 15 digits. It should work with for example 1.0e-5 for rounding numbers below 1.0e+10 but those ranges could not cover someone needs. The if solution would avoid all this, although I think comparing Reals with an specific value could be dangerous, I tend to use less than for that. eg intead if X = 0 i use IF ABS(x) < smallNumber being smallNumber less than the decimal presition used in x on the specific context.

Nice catch, that makes perfect sense.
I have revised the code in post marked as “Solution”

This also shows a problem of just writing to pass all of the test data
namely inputs and expected values that don’t cover all of the cases.
I’ve also added new test values for +7.0 and -7.0

Hi

REALs sometimes could behave differently as expected, for example look at this problem:

Traslated to Mark solution:

``````x real
CODE

IF Test( 7.03,  8,  7) THEN Pass += 1 ELSE Fail += 1 END
IF Test( 7.64,  8,  7) THEN Pass += 1 ELSE Fail += 1 END
IF Test( 0.12,  1,  0) THEN Pass += 1 ELSE Fail += 1 END
IF Test(-0.12,  0, -1) THEN Pass += 1 ELSE Fail += 1 END
IF Test(-7.1 , -7, -8) THEN Pass += 1 ELSE Fail += 1 END
IF Test(-7.6 , -7, -8) THEN Pass += 1 ELSE Fail += 1 END
IF Test(-7.0 , -7, -7) THEN Pass += 1 ELSE Fail += 1 END
IF Test( 7.0 ,  7,  7) THEN Pass += 1 ELSE Fail += 1 END

IF Test( (.5*3+.5*1.6)*100 ,  230,  230) THEN Pass += 1 ELSE Fail += 1 END   !passes

x =       .5*3+.5*1.6
IF Test(             x*100 ,  230,  230) THEN Pass += 1 ELSE Fail += 1 END   !fails with floor=229

MESSAGE('Passed['& Pass &']|Failed['& Fail &']','Tests complete')  !passed 9 failed 1``````

Yes small errors are often a problem when using REAL numbers.

I always suggest REALs should be avoided where you need an accurate answer.

1 Like

I agree, I would avoid passing DECIMAL to REAL if possible by overloading. Clarion’s BCD library was written to avoid issues with REAL’s a Decimal numbers. Something like 10.0 Decimal being 9.99999999 as a Real.

``````Floor(CONST *DECIMAL value),LONG

Floor(REAL value),LONG
``````

Could also prototype as a STRING and then convert to DECIMAL. Not the fastest but Clarion’s auto conversion works and (I think) no chance of a REAL oddity:

``````Floor(STRING valueStr),LONG
value DECIMAL(31,9)
CODE
value=ValueStr
...
``````

Code previously posted I thought could be simpler by setting to an INT() first then using that for comparisons. This has NOT been tested:

``````Ceiling PROCEDURE(REAL value) !,LONG
CODE
IF Answer <> Value AND Value > 0 THEN   !Was NOT an Integer and Positve
Answer += 1                          !So round up to higher Integer
END

Floor  PROCEDURE(REAL value) !,LONG