How to round to nearest quarter?

How might I accomplish rounding the decimal portion of a number (using plain AS) to the nearest quarter?
I wrote this, but it’s a little clunky. I’d prefer that 1.245 end up as 1.25, rather than 1.0.

``````
global isDecimal
property numvar : 1.245 as string

set setOff to offset of "." in numvar
set isDecimal to numvar's strings (setOff + 1) thru -1
set Whole to numvar's strings 1 thru setOff
enforceQuarter(isDecimal)

to enforceQuarter(NumString)
tell NumString
if it â‰¥ 0 and it < 25 then
set isDecimal to "0"
else
if it â‰¥ 25 and it < 50 then
set isDecimal to "25"
else
if it â‰¥ 50 and it < 75 then
set isDecimal to "50"
else
if it â‰¥ 75 and it â‰¤ 99 then
set isDecimal to "75"
end if
end if
end if
end if
end tell
end enforceQuarter

Whole & isDecimal
``````

``````set Num to 1.245
set theint to (round Num rounding down)
set thedec to (round ((Num - theint) * 4)) / 4
set thenum to theint + thedec

``````

Alternatively:

``````to roundToNearestQuarter(someNumber)
return result + ((round ((someNumber - result) * 4) rounding as taught in school) / 4)
end roundToNearestQuarter

roundToNearestQuarter(1.245)
--> 1.25
``````

Edit: Also:

``````to roundToDecimal(someNumber, theReciprocal)
return result + ((round ((someNumber - result) * theReciprocal) rounding as taught in school) / theReciprocal)
end roundToDecimal

roundToDecimal(1.245, 5)
--> 1.2
``````

:lol:
Applescript is downright funny sometimes!

If speed is important you might want to avoid round n. Because it is an OSAX command, it is much slower than a possible alternative: n as integer.

It looks like prior to AppleScript 1.9.2 (Mac OS X 10.3) coercing a non-integral real number to an integer was an invalid operation (old AppleScript Language Guide: “Only a real value that has no fractional part can be coerced to an integer.”), but these days it rounds (AppleScript 1.9.2 Release Notes: “Real numbers can now be coerced to integers even if they have a non-zero fractional part. Rounding is used to do the coercion.”). I find that as integer seems to round the same way that round n rounding to nearest rounds (including rounding 0.5 to the even side, not always away from zero).

So, I offer the following quarterRounder_3 and some result value comparisons and timing comparisons with cwtnospam’s and Bruce Phillips’s quarter-specific code:

``````to quarterRounder_1(Num)
set theint to (round Num rounding down)
set thedec to (round ((Num - theint) * 4)) / 4
set thenum to theint + thedec
end quarterRounder_1

return result + ((round ((someNumber - result) * 4) rounding as taught in school) / 4)
end quarterRounder_2

to quarterRounder_3(aNum)
return ((aNum * 4) as integer) / 4 -- In 10.3 and later, "n as integer" is like "round n rounding to nearest" (which is also the default for "round n")
end quarterRounder_3

to testThem(startNum, n, divisor)
script s
property testResults : {}
end script
repeat with testOffset from 0 to n
set testValue to startNum + testOffset / divisor
set {a, b, c} to {quarterRounder_1(testValue), quarterRounder_2(testValue), quarterRounder_3(testValue)}
set allEqual to a = b and b = c
if not allEqual then
set end of s's testResults to {value:testValue, |1|:a, |2|:b, |3|:c}
end if
end repeat
s's testResults
end testThem

to quarterRounder_0(aNum)
aNum
end quarterRounder_0

to timeThem(startNum, n, divisor)
set t0 to current date
repeat with testOffset from 0 to n
set testValue to startNum + testOffset / divisor
quarterRounder_0(testValue)
end repeat
set t1 to current date
repeat with testOffset from 0 to n
set testValue to startNum + testOffset / divisor
quarterRounder_1(testValue)
end repeat
set t2 to current date
repeat with testOffset from 0 to n
set testValue to startNum + testOffset / divisor
quarterRounder_2(testValue)
end repeat
set t3 to current date
repeat with testOffset from 0 to n
set testValue to startNum + testOffset / divisor
quarterRounder_3(testValue)
end repeat
set t4 to current date
set nullTime to t1 - t0
return {base:nullTime, |1|:t2 - t1 - nullTime, |2|:t3 - t2 - nullTime, |3|:t4 - t3 - nullTime}
end timeThem

on run
--return testThem(-2, 4 * 8, 8) -- demonstrate some discrepancies among the algorithms
--> {{value:-1.875, |1|:-2, |2|:-1.75, |3|:-2.0}, {value:-1.125, |1|:-1, |2|:-1.25, |3|:-1.0}, {value:-0.875, |1|:-1, |2|:-0.75, |3|:-1.0}, {value:-0.125, |1|:0, |2|:-0.25, |3|:0.0}, {value:0.125, |1|:0, |2|:0.25, |3|:0.0}, {value:0.875, |1|:1, |2|:0.75, |3|:1.0}, {value:1.125, |1|:1, |2|:1.25, |3|:1.0}, {value:1.875, |1|:2, |2|:1.75, |3|:2.0}}
--return testThem(2.375, 400, 200) -- more individual tests over a smaller numerical domain
--> {{value:2.875, |1|:3, |2|:2.75, |3|:3.0}, {value:3.125, |1|:3, |2|:3.25, |3|:3.0}, {value:3.875, |1|:4, |2|:3.75, |3|:4.0}, {value:4.125, |1|:4, |2|:4.25, |3|:4.0}}
timeThem(2.375, 400, 200)
--> {base:0, |1|:30, |2|:23, |3|:0}
--> {base:0, |1|:34, |2|:24, |3|:0}
--> etc.
end run
``````

For all the values I tested, 1 and 3 effectively return the same value (1 returns an integer when possible, 3 always returns a real). 2 uses a different rounding variation, so it differs on some of the eighths. At least on my machine though the speed difference is immense (1 takes about 30 seconds, 2 a bit less than 30 seconds (only one call to round) for 400 roundings; 3 takes less than 1 second for the same 400 roundings).

Model: iBook G4 933
AppleScript: 1.10.7
Browser: Safari Version 3.2.1 (4525.27.1)
Operating System: Mac OS X (10.4)

There are also these from my set of fast rounding handlers in ScriptBuilders:

``````(* Round to nearest multiple of q (q/2 away from zero) *)
on quantise(n, q)
(n * 2 div q - n div q) * q
end quantise

quantise(1.245, 0.25)

(* Round to nearest (.5 away from zero) *)
on rnd(n)
n div 0.5 - n div 1
end rnd

rnd(1.245 * 4) / 4
``````

Shortly after I got into AppleScript and joined the AppleScript-Users mailing list ” back in 1999, when it was Mac OS 8.6 and AS 1.3.7, and ‘round’ only had ‘rounding to nearest’, ‘rounding up’, and ‘rounding down’ parameters ” someone posted a complaint that rounding .5 to nearest sometimes rounded away from zero and sometimes towards zero. The ensuing debate polarised list members into those who considered that the convention of rounding .5 to the nearest even whole number was a standard of the Institute of Electrical and Electronic Engineers, Inc. and therefore correct, and those who’d never heard of that convention and expected .5 to round away from zero. I was one of the latter group and demanded that ‘round’ be fixed “for those who learned their maths at school”. A few months later, the next Standard Additions update gave ‘round’ its ‘rounding as taught in school’ parameter. The name probably wasn’t inspired my post, but I’ve felt bad about it ever since. It was that, and the discovery that vanilla handlers were very much faster, that inspired me to write the collection linked above.

All of these are great. Thanks, guys.