The idea of my script is to increase prices in a Quark document by the requested percentage increase, I managed to get the script to work except for rounding the number to the required 2 decimal places, after some research I discovered the round truncate and number to string handlers which I have included in my script. Here is the script:
--Number to string handler
on number_to_string(this_number)
set this_number to this_number as string
if this_number contains "E+" then
set x to the offset of "." in this_number
set y to the offset of "+" in this_number
set z to the offset of "E" in this_number
set the decimal_adjust to characters (y - (length of this_number)) thru -1 of this_number as string as number
if x is not 0 then
set the first_part to characters 1 thru (x - 1) of this_number as string
else
set the first_part to ""
end if
set the second_part to characters (x + 1) thru (z - 1) of this_number as string
set the converted_number to the first_part
repeat with i from 1 to the decimal_adjust
try
set the converted_number to the converted_number & character i of the second_part
on error
set the converted_number to the converted_number & "0"
end try
end repeat
return the converted_number
else
return this_number
end if
end number_to_string
--Round truncate handler
on round_truncate(this_number, decimal_places)
if decimal_places is 0 then
set this_number to this_number + 0.5
return number_to_text(this_number div 1)
end if
set the rounding_value to "5"
repeat decimal_places times
set the rounding_value to "0" & the rounding_value
end repeat
set the rounding_value to ("." & the rounding_value) as number
set this_number to this_number + rounding_value
set the mod_value to "1"
repeat decimal_places - 1 times
set the mod_value to "0" & the mod_value
end repeat
set the mod_value to ("." & the mod_value) as number
set second_part to (this_number mod 1) div the mod_value
if the length of (the second_part as text) is less than the decimal_places then
repeat decimal_places - (the length of (the second_part as text)) times
set second_part to ("0" & second_part) as string
end repeat
end if
set first_part to this_number div 1
set first_part to number_to_string(first_part)
set this_number to (first_part & "." & second_part)
return this_number
end round_truncate
--Request percentage increase, retrieve number from Quark text box and apply increase
property the_choice : 10
tell application "QuarkXPress"
set thePercentage to (display dialog "What percentage increase do you wish to apply" buttons {"Cancel", "OK"} default button "OK" default answer the_choice)
if button returned of thePercentage is "Cancel" then error "User canceled." number -128
set the_choice to the text returned of the thePercentage
set the_choice to (the_choice / 100 + 1)
tell current box of document 1
try
set ToChange to last word
set Changed to (ToChange * the_choice)
end try
end tell
end tell
--Round the number to 2 decimal places
round_truncate(Changed, 2)
--Return rounded number to Quark and update text box
tell application "QuarkXPress"
tell current box of document 1
set the last word to Changed
end tell
end tell
When the script is run without attempting to return the number to Quark I get the correct number displayed in the results box of applescript editor rounded to 2 decimal places. As soon as I include the return number to Quark and update text box part of my script, the number displayed in the text box within quark has been calculated correctly but hasn’t been rounded to the 2 decimal places. Any help with this would be much appreciated.
I made a new handler to round numbers, that for all I know, should be as fast as the one used, but slightly shorter.
Edit
Added consideration of zero decimals, and zero decimal places.
Edit++
Added a test for division by zero. And, discoverered that it rounds .5 to zero, and not 1. See below for “usual_rounding”, which supports the regular way of rounding numbers.
And corrected the coercion to happen after the test.
set mnum to 0
set rnum to bankers_rounding(mnum, 3)
to bankers_rounding(aNumber, decimalPlaces)
set factor to (10 ^ decimalPlaces)
set aNumber to round (aNumber * factor)
if aNumber ≠0 then set aNumber to aNumber / factor
set aNumber to aNumber as text
-- fills out missing zeros first we need decimal separator
if decimalPlaces > 0 then
set dec_sep to text 2 of (1 / 2 as text)
set numlen to length of aNumber
set decpos to offset of dec_sep in aNumber
if decpos = 0 then
set aNumber to aNumber & dec_sep
set numlen to numlen + 1
set decpos to numlen
end if
repeat (decimalPlaces - (numlen - decpos)) times
set aNumber to aNumber & "0"
end repeat
else
set aNumber to aNumber as integer as text
end if
return aNumber
end bankers_rounding
It is properly tested, I had to fix the fix for division by zero.
This one I believe rounds more in terms with the OP’s wishes, as it rounds .5 to 1, and not zero.
set mnum to 14.3445
set rnum to usual_rounding(mnum, 3)
-- http://macscripter.net/viewtopic.php?pid=165353#p165353
to usual_rounding(aNumber, decimalPlaces)
set factor to (10 ^ decimalPlaces)
set aNumber to round (aNumber * factor) rounding as taught in school
if aNumber ≠0 then set aNumber to aNumber / factor
set aNumber to aNumber as text
-- fills out missing zeros first we need decimal separator
if decimalPlaces > 0 then
set dec_sep to text 2 of (1 / 2 as text)
set numlen to length of aNumber
set decpos to offset of dec_sep in aNumber
if decpos = 0 then
set aNumber to aNumber & dec_sep
set numlen to numlen + 1
set decpos to numlen
end if
repeat (decimalPlaces - (numlen - decpos)) times
set aNumber to aNumber & "0"
end repeat
else
set aNumber to aNumber as integer as text
end if
return aNumber
end usual_rounding
I had made one more bug, when the number of decimal places was zero. Post #7 and #8 are fixed.
Below is a general handler, that takes AppleScript’s parameter for rounding as the third argument.
set num to rounding(0.5, 0, as taught in school)
-- http://macscripter.net/viewtopic.php?pid=165356#p165356
to rounding(aNumber, decimalPlaces, round_method)
-- round_method can be: (AppleScript Language Guide p. 149)
-- up: same as ceiling 14.5 --> 15
-- down: same as floor 14.6 --> 14
-- toward zero: discards fractional parts.
-- to nearest: (bankers rounding) 1.5 --> 2 0.5 --> 0
-- (rounds towards nearest *even* integer.)
-- as taught in school: Rounds to nearest integer
-- (0.5 is rounded upwards.)
set factor to (10 ^ decimalPlaces)
set aNumber to round (aNumber * factor) rounding round_method
if aNumber ≠0 then set aNumber to aNumber / factor
set aNumber to aNumber as text
-- fills out missing zeros first we need decimal separator
if decimalPlaces > 0 then
set dec_sep to text 2 of (1 / 2 as text)
set numlen to length of aNumber
set decpos to offset of dec_sep in aNumber
if decpos = 0 then
set aNumber to aNumber & dec_sep
set numlen to numlen + 1
set decpos to numlen
end if
repeat (decimalPlaces - (numlen - decpos)) times
set aNumber to aNumber & "0"
end repeat
else
set aNumber to aNumber as integer as text
end if
return aNumber
end rounding