Mailing List Archive: 49091 messages

## [REBOL] Re: ROUND function (like TRUNC, FLOOR, etc...)

### From: greggirwin:mindspring at: 15-Feb-2002 10:53

<< I'm developing a program in my company, and an engineer asked how I round the numbers... but... I discovered (I'm still a newbie...) Rebol has only TO-INTEGER function to round a number! No FLOOR, no CEIL, no TRUNC! And... how about Banker's algorithm to round numbers? >> Here's what I came up with, based on some of Ladislav's excellent work. Watch for word-wrap. ; Ladislav Mecir, Gregg Irwin (minor adjustment) 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] ;-- Alternate implementation ;value1 // value2 + (value2: abs value2) // value2 ][ value1 // value2 ] ] ;-- Note: to-interval does mod-like rounding. If the interval you ; specify is not evenly divisble into your base, the result ; may not be what you expect. E.g. round/to-interval 133 30 ; will round to 120, not 130, because 120 is an even multiple ; (read interval) of 30. ; Ladislav Mecir, Gregg Irwin round: func [ {Rounds numeric value with refinements for what kind of rounding you want performed, how many decimal places to round to, etc.} value [number! money! time!] {The value to round} /up {Round away from 0} /floor {Round towards the next more negative digit} /ceiling {Round towards the next more positive digit} /truncate {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] ] HTH! --Gregg