Formatting a number

I am hoping someone has already devised a more efficient way to accomplish this. I want to include the file size of a bunch of photos in a catalog database. Of course, I don’t want some 7 or 8 digit thing there, I want something like 1.23 MB or 0.567 MB instead. So, I came up with this handler, which receives the size of the file as determined by the Finder, and returns a 3 digit number, so long as it is over 1.0 MB:

on MakeNumber(n)
	set nm to (((n) / 1048576) * 100 as integer)
	set ns to nm as string
	set nl to every character of ns
	set fl to items 1 thru -3 of nl
	set fll to fl & "." & (items -2 thru -1 of nl)
	set fn to fll as string
	return fn as real
end MakeNumber

The only way I could devise a modification to catch numbers less than 1 MB was this:

on MakeNumber(n)
	set nm to (((n) / 1048576) * 10000 as integer)
	set ns to nm as string
	set nl to every character of ns
	set fl to items 1 thru -4 of nl
	set fll to fl & "." & (items -2 thru -1 of nl)
	set fn to fll as string
	fn as real
	return (fn / 10)
end MakeNumber

Essentially, all it does is add a 4th digit to the number, then moves the decimal over one spot to the left on the return.

It all works, but I am completely open to suggestions from anyone on a more efficient method that would cover a range of file sizes from about 400 KB up to over 25 MB.

Assuming 3 digit accuracy for files of more than 100K and less than 100M, I’d use:

tell application "Finder"
	set S to (size of (choose file)) / (1048576) as text
end tell
set Num to characters 1 thru 4 of S as string as number

EDIT: If you like one-liners:

tell application "Finder" to tell ((size of (choose file)) / 1048576 as text) to characters 1 thru 4 of it as string as number

Hi, Craig.

I wrote this a couple of years ago. Is it what you mean?

on convertByteSize(byteSize)
	if byteSize ≥ 1.099511627776E+12 then -- Terabytes (2 ^ 40)
		((byteSize / 1.099511627776E+12 * ((10.0 ^ 0.5) ^ 2) div 0.1 / 100.0) as string) & " TB"
	else if byteSize ≥ 1.073741824E+9 then -- Gigabytes (2 ^ 30)
		((byteSize / 298496 * sectors * ((10.0 ^ 0.5) ^ 2) div 0.1 / 100.0) as string) & " GB"
	else if byteSize ≥ 1048576 then -- Megabytes (2 ^ 20)
		((byteSize / 1048576 * ((10.0 ^ 0.5) ^ 2) div 0.1 / 100.0) as string) & " MB"
	else if byteSize ≥ 1024 then -- Kilobytes (2 ^ 10)
		((byteSize div 1024) as string) & " K"
	else
		(byteSize as string) & " bytes"
	end if
end convertByteSize

“TB”, “GB”, and “MB” figures are truncated to two decimal places, and “K” figures rounded down to the nearest kilobyte. The business with the squares and square roots is an attempt to shake out floating-point errors that occur with certain numbers in the two-decimal-place process.

Adam:

Perfect! I knew I was trying too hard. Nice one-liner, by the way. This is pretty much precisely what I was looking for.

Nigel:

Very nice, I especially like the K, MB, and GB add-ons. I have filed it away for later use. I want this particular project to have all the numbers in the same reference ranges, that it, 1.0 is 1 MB.

Happy Boxing Day to you all.

I have no doubt that Nigel could cram a selection of K or M into the one-liner: 2.34K or 1.23M for examples, but I couldn’t, so they’re all in MB.

Hi guys.

It’s probably worth noting that, in certain cases (for example, if the file size is an exact multiple of 1MB), the resulting string in Adam’s script may be less than 4 characters - which will result in an error. In addition, if a calculated value is expressed as scientific notation, then the subsequent coercions will produce an inaccurate result.

You might therefore like to consider something more like this:

tell application "Finder" to ((choose file)'s size) / 1.048576E+4 div 1 / 100

kai:

Flawless, as usual, sir. And, as is my wont, I am searching for meaning. I assume that this works because of the order AS performs certain functions, but I am at a loss to understand the flow. For instance, if you change the 1/100 to 0.01, it returns the incorrect number:

tell application "Finder" to ((choose file)'s size) / 1.048576E+4 div 1 / 100

--->2.2

tell application "Finder" to ((choose file)'s size) / 1.048576E+4 div 0.01

--->22092

So, I am led to believe that the operator between the 1 and 100 is interpreted specifically by AS, whereas the result of that small computation returns an incorrect number. I have believed that the [div] command was used when one wanted to find the ‘rounded’ result of a division operation, like in the div days and div weeks functions. With that understanding, should not 1/100 be interpreted the same as 0.01?

I fully admit I did not survive advanced mathematics (I failed my first Calculus examination and dropped the class) and stopped after college algebra/trigonometry. And yet, this seems to be nothing more than an exercise in algebra, notation, and how AS performs mathematical operations, which should be understandable.

I know you are a busy person, kai, but I would appreciate another mini-lecture if ever you have the time.

Sure thing, Craig - I’ll give it a shot. :slight_smile:

For slightly faster execution, it’s an idea to evaluate any operations that can be pre-calculated - so that only those dependent on the values of variables are left to perform at runtime. I thought my suggestion had pretty much covered all that could be done in advance - so I’d have been a little disappointed with myself if any further such opportunities came to light. :wink:

To appreciate what’s going on here, it’s important to understand AppleScript’s rules for operator precedence, which can be summarised (for mathematical operations) as:

  1. All items in parentheses : innermost to outermost operations

  2. Plus or minus signs for numbers

  3. Exponentiation (^) : right to left

  4. Multiplication and division (*, /, div, mod) : left to right

  5. Addition and subtraction (+, -) : left to right

In this particular situation, there are only 3 operations (all division) to be performed - so these are simply carried out from left to right. If we were to do the same thing in a step-by-step script, it might look something like this:

2.3165140992E+6 (* Finder simulation *)

result / 1.048576E+4
--> 220.92

result div 1
--> 220

result / 100
--> 2.2

Alternatively, using parentheses - purely to clarify precedence:

((2.3165140992E+6 / 1.048576E+4) div 1) / 100
--> 2.2

Incidentally, the 1.048576E+4 (pre-calculated from 1028 ^ 2 / 100) is obviously used to avoid multiplying the value of the incoming size by 100:

(((2.3165140992E+6 * 100) / 1048576) div 1) / 100
--> 2.2

As you can see, the div operator is indeed used to truncate the value to an integer (which is faster than rounding down using the round function).

So, while 1 / 100 appears at the end of the statement, that’s not how AppleScript reads it. Of course, if we force it to, we’ll get the same result that you did earlier (since the operation would certainly evaluate to 0.01):

2.3165140992E+6 (* Finder simulation *)

result / 1.048576E+4
--> 220.92

result div (1 / 100) --> 0.01
--> 22092

Or:

(2.3165140992E+6 / 1.048576E+4) div (1 / 100)
--> 22092

This not only changes the rounding but, since the final operation effectively multiplies (rather than divides) by 100, the result is some 10,000 times greater than expected.

I hope that helps to clarify the behaviour a little. If I’ve missed anything (or added to the confusion), just yell. :slight_smile:

Also, of course, divving a division result by 1 has the same effect as doing a ‘div’ in the first place, so that can be further reduced to:

(2.3165140992E+6 div 1.048576E+4) / 100
--> 2.2

-- Or:
2.3165140992E+6 div 1.048576E+4 / 100

:slight_smile:

Of course, Mr G. :lol:

I remain, as always, blown away. I learn more from these exchanges than from the questions I ask myself.

Beautiful, gentlemen, absolutely beautiful. Thank you again for the concise explanation, kai, it is perfectly clear for me now. I was initially impressed with the moving of the decimal on the 1.048576E+4 from the original 1048576 number, as that reduced a step already in itself, and now the div usage makes perfect sense as well.

And kudos to Nigel for even further reduction! My American trained mathematic brain would not have seen that for at least 6 weeks, it at all.

Thank you all very much. As Adam remarked, this thread turned into a unexpected pleasure.