Testing the quickest way to retrieve the macOS name (Sonoma, etc.)

That uses the same approach as my approach above.

Does it? It looks like yours uses grep and the "major" portion of the sw_vers result, while mine does string comparison of sw_vers -productVersion against the "Latest version" numbers (from this page).

All I meant was that we both used the sw_vers command and used a fixed list of macOS versions to find the best match. That seems to be the case in both macros. Sorry if the comparison bothers you.

I have a question for you. I like the way you used "is before." According to the KM documentation, that is supposed to mean "is alphabetically before." but look at the following: (notice it evaluates to TRUE)

image

Alphabetically, 2 is not before 14, as you can see here: (notice it evaluates to FALSE)

image

So this seems to show that KM is using numerical comparisons, not alphabetical comparisons, for "is before". If they were alphabetical comparisons, then 14 would be before 2, just as "AD" is before "B." But digits are resulting in a different result than alphabet characters!

I had no idea this feature existed. It is a great feature, and I'm amazed that you found it. How did you know about that? I have to resurrect my idea that you must be an alias for The Architect, because only an architect would know about undocumented features, and use them to solve problems. You are using an undocumented feature here to write your code. I'm very impressed.

It doesn't bother me. The point is that we used completely different methods to search the list, arguably the "meat" of the macro.

We're writing a macro, we've got our programmer's head on -- naturally we're going to head for grep or similar to find something in a list. But now imagine you've a long, ordered, list on a piece of paper -- would you search search each line in turn looking for a complete match, or would you scan down to match the first digit then continue down to match the second etc?

I just thought it would fun to present the second approach :wink: -- I've no idea if it is better or worse, though!

My guess is that KM is using the OS string comparison framework -- the same one the Finder uses that puts "2 Folder" before "13 Folder" when you sort by name. And yes, that means that 10.9 is before 10.10...

Your "guess" was astounding. In a million years I never would have guessed that a "string comparison" was actually a "numerically sensitive sorting algorithm." Had I know that, I would very likely have used your approach. The documentation is wrong and you knew that and you took advantage of that fact to solve the problem.

I would do whatever is simplest to write, understand and maintain. The "meat" of my solution is a single shell command, grep "^$KMVAR_LocalVersion". It's hard to get simpler than that.

Amazingly, there's actual Apple documentation about this, at least at the Finder level:

Filename Sorting Rules


The Finder’s sort order for file and directory names is based on the Unicode Collation Algorithm (Technical Standard UTS #10) defined by the Unicode Consortium. That standard provides a complete and unambiguous sort ordering for all Unicode characters and is available on the Unicode Consortium website (http://www.unicode.org). The Finder alters the default sorting behavior of this algorithm slightly by taking advantage of some sanctioned alternatives, specifically:

  • Punctuation and symbols are significant for sorting.
  • Substrings of digits are sorted according to their numeric value, as opposed to sorting the actual characters in the number.
  • Case is not considered during sorting.

It seems Keyboard Maestro uses the macOS method of file sorting, which makes sense.

If my memory is correct, this change happened with the move from Mac OS 9 to Mac OS X Public Beta. Before Mac OS X, the Finder sorted strictly on characters, where anything with "1" would come before anything with "2".

If my memory is wrong, I'm sure some other long-termer will correct me :slight_smile:.

-rob.

It's a guess about how it works. Finding out in the first place was probably a serendipitous mistake when trying to do something else!

Spoken like a true programmer :wink:

To be fair, the docs do say it is "alphabetically before/after" -- and, even though number order is undefined, I think that many people in a "non-programming" situation would consider that 2 comes before 11 just as aardvark comes before Ant.

Unfortunately the Filter action's "Sort Lines" appears to use ASCII ordering, where the opposite would be true, so you'll have to roll your own routine if you want the same "natural" behaviour when sorting lines of text.

I agree with you that that's what KM actually does. Indeed, I was the one that identified this and explained in this thread that the condition does that, as opposed to what the KM documentation says. Here's the KM documentation:

The Text condition evaluates a text token string and then checks if it:

  • is empty.
  • is not empty.
  • is exactly (case insensitively) a specified string.
  • is not exactly (case insensitively) a specified string.
  • contains (case insensitively) a specified string.
  • does not contain (case insensitively) a specified string.
  • starts with (case insensitively) a specified string. (v8+)
  • ends with (case insensitively) a specified string. (v8+)
  • is alphabetically before a specified string. (v7.2+) <-------------------- HERE

Not only does it make no mention whatsoever that this is not an alphabetical sort, but further down on the page it contrasts with a numerical sort:

  • is alphabetically after a specified string. (v7.2+)
  • matches a specified regular expression.
  • does not match a specified regular expression.
  • is numerically less than a specified calculation. (v8+) <-------------- HERE

Why would any objective reader of this documentation think that an "alphabetical sort" does numerical string comparisons when there's a numerical string comparison condition just four lines down the page?

Sorry, I wasn't meaning to imply anything about your post (or anyone's!), was just providing some historical context about the OS itself and its own issues with sorting.

I should have been clearer, but for an excuse, I had to drive to the airport at 4am, so I'll use that :).

-rob.

1 Like

Because that's not a numerical string comparison, just a numerical one. When you choose that or the following options the edit box changes to a calculation field.

The documentation may not be complete, in that it doesn't define "alphabetically" -- but it isn't wrong.

Numbers are stored as strings, not integers. So indeed it is a string comparison interpreting the string as a number. I.e, a numerical string comparison.

You can't determine how numbers are represented, or evaluated, at execution time based on how they are stored. And, given that KM very helpfully casts to the best "class" as required by context, it doesn't really matter.

If you are going to argue that, during execution, this:

is storing binary 00110001 in memory and not 00000001, I'd love to see the evidence.

Remember that in your example above you are explicitly comparing text. Whatever routine KM is using for that appears to follow Apple's "Finder rules" for string comparison, with the rules @griffman referenced. That has no bearing on how KM treats explicit numbers, such as the results of a calculation or function evaluation.

Yes, that's 100% exactly what I'm saying. However when you say "in memory" I'm interpreting that as the memory storage area for the KM variable itself, not as any temporary Engine work area used to evaluate expressions. Let me explain that in more details. For example, in this action:

... the string "1+2" is analyzed by the KM Engine, which converts the string "1" in an IEEE 754 double precision temporary storage location, and does the same thing for the string "2", then uses a real number add operation to add the two values, then converts the resulting IEEE 754 value back into a string which is where MyVar stores the string value "3".

Read the following page which says "all variables are just strings." That means all variables, including any variable containing "numbers" such as you provided above.

https://wiki.keyboardmaestro.com/manual/Variables

There are also posts on this website where The Architect explains this. At this moment I'm unable to find them.

The only time a numeric variable is NOT stored as a string is when the Engine is converting these strings to IEEE 754 double precision values. This is temporary, probably just for a millisecond, and is not ever how any variables "store" their numbers, including in the example you gave.

By the way, the value "1" when stored in IEEE 754 doesn't look anything like "00000001" it looks like this: "3f800000". You can check that yourself here at this conversion website:

https://www.h-schmidt.net/FloatConverter/IEEE754.html

That paragraph is a sub-section of "Variables in Text Fields" -- it's setting the stage for pseudo arrays. I'm not saying you're wrong, just that we need to consider the context of that statement.

But you may well be right -- I struggling to find something to prove otherwise and which doesn't have precision/casting problems, as illustrated by:

Deep waters -- and thank you, @Airy, for making me swim in them!

There is more information in this thread, including explanations from The Architect.

I think I was wrong on one point. The KM Engine uses both double precision floating point and 64-bit integer formats for some calculations in the Engine. I'm not sure how to determine the occasions that it chooses each format. Most likely it sticks to the 64-bit Integer format unless the string is over 15 digits or contains a decimal point.

Your snapshot above shows 7 digits of precision failing to give the correct result. I'm not sure how that could be, since the KM Engine doesn't use Single precision floating point.

Thanks, @Airy.

Peter's first post in that thread also states that KM "uses double to store values internally" -- so I guess the question is "at what point do they become 'internal', and for how long?".

Which is why I'm starting to doubt my own sanity :wink: But it's reproducible, even within a calculation (so probably not a string->number conversion issue):

At which point I have to assume problems at my end -- or problems with me!

I infer that this means for doing calculations. As I said earlier, I believe that this is for calculations only, not permanent storage.

If Peter doesn't want to reveal the details, that's okay. What we need to know is how to use KM, not how it works internally, which can change from time to time anyway.

Your second example confirms your first example, which is that the precision the KM engine can deal with before converting to real numbers is equivalent to single precision float values.

Keyboard Maestro uses doubles when doing numerical calculations.

doubles are not stored permanently, since variables are strings. So any result is converted to a string to store it in a variable.

There is a degree of rounding to ensure that you don't get bad results for normal operations (like 10/5 giving 1.9999999999999 for example).

Thanks, Peter. And to @Airy for making me question my assumptions.

I tip my hat to @Airy, withdraw all my incorrect statements from the above, and retire to ponder...