## [REBOL] Rounding function and related mod function

### From: greggirwin::mindspring::com at: 7-Nov-2001 11:01

Hi Gang, Using Ladilsav's rounding functions as a starting point, along with the the comments posted here, behold...the kitchen sink! Let me know if it produces incorrect results (and please send me the test case data) or just what you think. What missing? What's superfluous? What's confusing? mod: func [ {Compute a remainder.} value1 [number! money! time!] {The dividend} value2 [number! money! time!] {The divisor} /euclid {Compute a non-negative remainder such that: a = qb + r and r < b} /local r ] [ either euclid [ either negative? r: value1 // value2 [r + abs value2] [r] ][ value1 // value2 ] ] round: func [ value [number! money! time!] {The value to round} /up {Round away from 0} /floor {Round down, towards the next more negative digit} /ceiling {Round up, towards the next more positive digit} /truncate {Round down. Remaining digits are unchanged. (a.k.a. down)} /places {The number of decimal places to keep} pl [integer!] /to-interval {Round to the nearest multiple of interval} interval [number! money! time!] /local factor ][ ;-- places and to-interval are redundant. E.g.: ; places 2 = to-interval .01 ; to-interval is more flexible so I may dump places. ;-- This sets factor in one line, under 80 chars, but is it clearer? ;factor: either places [10 ** (- pl)][either to-interval [interval][1]] factor: either places [ 10 ** (negate pl) ][ either to-interval [interval][1] ] ;-- We may set truncate, floor, or ceiling in this 'if block. if not any [up floor ceiling truncate] [ ;-- Default rounding is even. Should we take the specified ; decimal places into account when rounding? We do at the ; moment. either (abs value // factor) <> (.5 * factor) [ value: (.5 * factor) + value return value - mod/euclid value factor ][ ;-- If we get here, it means we're rounding off exactly ; .5 (at the final decimal position that is). either even? value [ truncate: true ][ either negative? value [floor: true][ceiling: true] ] ] ] if up [either negative? value [floor: true][ceiling: true]] if truncate [return value - (value // factor)] if floor [return value - mod/euclid value factor] if ceiling [return value + mod/euclid (negate value) factor] ] Here are a few test cases: ;-- Even rounding round -2.5 round -1.5 round -0.5 round 0.5 round 1.5 round 2.5 ;-- Round to the nearest quarter round/to-interval 10.87 .25 round/to-interval 10.88 .25 round/to-interval -10.87 .25 round/to-interval -10.88 .25 ;-- Round to the nearest hundred round/truncate/to-interval 5555.5 100 round/up/to-interval 5555.5 100 Here are some possibilities for alternate help text for refinements: /euclid {Find R such that A = QB + R and 0<=R<ABS (B)} /floor {Round downwards} /ceiling {Round upwards} /truncate {Round towards zero} I'm open to any suggestions, particularly those that will make it easier to use and/or easier to understand the behavior of the various options. Thanks! --Gregg