Monday, December 9, 2019

#1 2019-12-01 11:03:47 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 5118

JXA vs. AppleScript - round

The only built-in JavaScript rounding function I've found so far is Math.round(). The macOS version of this uncompromisingly rounds .5 up, regardless of sign or nearest even. It's as if it simpy adds 0.5 and truncates the result:

Applescript:

// JavaScript code.

Math.round(4.5); // --> 5
Math.round(-4.5); // --> -4

By contrast, AppleScript's 'as integer" coercion and the StandardAdditions 'round' command both observe the IEEE standard whereby .5 is rounded to the nearest even integer. And even when rounding 'as taught in school', 'round' rounds both positive and negative .5s away from 0:

Applescript:

-- AppleScript code.

4.5 as integer --> 4
5.5 as integer --> 6
-4.5 as integer --> 4
-5.5 as integer --> -6

round 4.5 --> 4
round -4.5 --> -4
round 4.5 rounding as taught in school --> 5
round -4.5 rounding as taught in school --> -5

If AppleScript-like rounding is required in a JXA script, it's possible to use the StandardAdditions 'round':

Applescript:

// JavaScript code.

var app = Application.currentApplication();
app.includeStandardAdditions = true;

app.round(4.5) // --> 4
app.round(4.5, {rounding: "as taught in school"}) // -> 5

But somewhat faster, and usable in "vanilla" JavaScript as well, are these home-grown functions:

Applescript:

// JavaScript code.

function round(n) {
   return (((n % 2) ** 2 > 0.25) ? roundAsTaughtInSchool(n) : Math.trunc(n));
};

function roundAsTaughtInSchool(n) {
   return Math.trunc(n * 2) - Math.trunc(n) ;
};

round(4.5); // --> 4
round(5.5); // --> 6
roundAsTaughtInSchool(-4.5); // --> -5

The AppleScript equivalents are:

Applescript:

-- AppleScript code.

on |round|(n)
   if ((n mod 2) ^ 2 > 0.25) then
       return roundAsTaughtInSchool(n)
   else
       return n div 1
   end if
end |round|

on roundAsTaughtInSchool(n)
   return n div 0.5 - n div 1
end roundAsTaughtInSchool

|round|(4.5) --> 4
|round|(5.5) --> 6
roundAsTaughtInSchool(-4.5) --> -5


NG

Offline

 

#2 2019-12-01 06:40:02 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6065

Re: JXA vs. AppleScript - round

Nigel Garvey wrote:

The only built-in JavaScript rounding function I've found so far is Math.round().



Arguably Math.ceil() and Math.floor() are also rounding functions.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#3 2019-12-02 03:26:15 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 5118

Re: JXA vs. AppleScript - round

Shane Stanley wrote:

Arguably Math.ceil() and Math.floor() are also rounding functions.


I'd regard those more as "nudge" functions.  wink  I think the woman who asked the estate agent about my house on Friday must have used the latter several times when working out what fraction of the asking price she'd be able to offer.  hmm

But OK. "The only built-in JavaScript function I've found so far which rounds a fractional number either up or down to whichever adjacent integer is the nearer and for which what to do about numbers exactly half-way between those integers is relevant is Math.round()."  tongue


NG

Offline

 

#4 2019-12-02 04:53:14 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6065

Re: JXA vs. AppleScript - round

I think the real estate version is actually the nudge-nudge function smile.

I did say "arguably", although I think any function that turns floating-point values into round numbers has a prima facie case for inclusion in the rounding category wink.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#5 2019-12-03 04:01:28 pm

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 5118

Re: JXA vs. AppleScript - round

Here are some JavaScript functions to round one number to the "nearest" multiple of another. If the other number's omitted from the call parameters (which JXA doesn't seem to mind), the default used is 1, for "nearest integer".

Applescript:

/*    JXA script.
   Round n in various ways to an adjacent multiple of q.
   For 'nearest integer', the q parameter should be 1.
   However, JXA doesn't mind if parameters are omitted from calls. If the q parameter's omitted, the functions adopt a default q of 1.
   There's obviously no point in omitting the n parameter as well.
   JXA returns whole-number values in integer form.
   The functions below return 'NaN' when meaningful results aren't possible.
*/

// To nearest multiple, IEEE 754 style. (Midway values to nearest /even/ multiple.)
function round(n, q) {
   if (q == undefined) {q = 1;}; // Default to 1 if q not given.
   if (q == 0) {return q;}; // The only possible result if q's 0.
   // If n is more than a quarter of the way (zerofugally) between two consecutive even multiples of q, round "as taught in school".
   if ((n % (q * 2)) ** 2 > q * q / 4) {return n + n % q - (n * 2) % q;};
   // Otherwise truncate.
   return n - n % q;
};

// To nearest multiple, "as taught in school". (Midway values to nearest multiple away from zero.)
function roundAsTaughtInSchool(n, q) {
   if (q == undefined) {q = 1;};
   if (q == 0) {return q;};
   return n + n % q - (n * 2) % q; // (Math.trunc(n * 2 / q) - Math.trunc(n / q)) * q ;
};

// To nearest multiple zerowards.
function truncate(n, q) {
   if (q == undefined) {q = 1;};
   if (q == 0) {return q;};
   return n - n % q; // Math.trunc(n / q) * q;
};

// To nearest multiple away from zero.
function extend(n, q) {
   if (q == undefined) {q = 1;};
   if ((q == 0) && (n == 0)) {return q;};
   if (n < 0) {return Math.floor(n / q) * q;};
   return Math.ceil(n / q) * q;
};

// To nearest multiple below.
function floor(n, q) {
   if (q == undefined) {q = 1;};
   if ((q == 0) && (n >= 0)) {return q;};
   return Math.floor(n / q) * q;
};

// To nearest multiple above.
function ceiling(n, q) {
   if (q == undefined) {q = 1;};
   if ((q == 0) && (n <= 0)) {return q;};
   return Math.ceil(n / q) * q;
};

// Examples:
round(123456.5) // or round(123456.5, 1) // --> 123456 (nearest integer, IEEE 754 standard)
roundAsTaughtInSchool(123456.5) // or roundAsTaughtInSchool(123456.5, 1) // --> 123457 (ditto, "as taught in school")
round(123456.5, 100) // --> 123500 (nearest hundred)
round(1.234567, 0.001) // --> 1.235 (three decimal places)
truncate(123456.5, 1000) // --> 123000 (nearest thousand towards zero)
extend(123456.5, 1000) // --> 124000 (nearest thousand away from zero)
floor(123456.6, 1000) // --> 123000 (nearest thousand below)
floor(-123456.6, 1000) // --> -124000 (ditto)

And the AS equivalents. Both parameters have to be specified in these:

Applescript:

(*    AppleScript script.
   Round n in various ways to an adjacent multiple of q.
   For 'nearest integer', the q parameter should be 1.
   THE q PARAMETER ISN'T OPTIONAL HERE as it's less trouble to type ", 1" than to use AS's optional-parameter methods!
   Some of the math is slightly different from that in the JXA script to ensure that the AS results are in the same number class as q.
   The last three handlers below return 'missing value' when meaningful results aren't possible.
*)

-- To nearest multiple, IEEE 754 style. (Midway values to nearest /even/ multiple.)
on |round|(n, q)
   if (q = 0) then return q -- The only possible result if q's 0.
   -- If n is more than a quarter of the way (zerofugally) between two consecutive even multiples of q, round "as taught in school".
   if ((n mod (q + q)) ^ 2 > q * q / 4) then return ((n + n) div q - n div q) * q
   -- Otherwise truncate.
   return n div q * q
end |round|

-- To nearest multiple, "as taught in school". (Midway values to nearest multiple away from zero.)
on roundAsTaughtInSchool(n, q)
   if (q = 0) then return q
   return ((n + n) div q - n div q) * q
end roundAsTaughtInSchool

-- To nearest multiple zerowards.
on truncate(n, q)
   if (q = 0) then return q
   return n div q * q
end truncate

-- To nearest multiple away from zero.
on extend(n, q)
   if (q = 0) then
       if (n = 0) then return q
       return missing value
   end if
   set nTruncated to n div q * q
   if (nTruncated = n) then return nTruncated
   if (q < 0) then set q to -q
   if (n < 0) then return nTruncated - q
   return nTruncated + q
end extend

-- To nearest multiple below.
on floor(n, q)
   if (q = 0) then
       if (n < 0) then return missing value
       return q
   end if
   set nTruncated to n div q * q
   if (nTruncated > n) then
       if (q < 0) then set q to -q
       return nTruncated - q
   end if
   return nTruncated
end floor

-- To nearest multiple above.
on ceiling(n, q)
   if (q = 0) then
       if (n > 0) then return missing value
       return q
   end if
   set nTruncated to n div q * q
   if (nTruncated < n) then
       if (q < 0) then set q to -q
       return nTruncated + q
   end if
   return nTruncated
end ceiling

-- Examples:
|round|(1.234565E+5, 1) --> 1234356 (nearest integer, IEEE 754 standard)
roundAsTaughtInSchool(-1.234565E+5, 1) --> -123457 (ditto, "as taught in school")


NG

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)