Dynamo is built on the DesignScript (DS) language. Graphs are first compiled into DS before they are executed in the DS virtual machine.
DesignScript like any other programming language offers its own types, operators, language constructs and a standard library. Out of the number types, DS has support for integer (whole numbers) and double primitive data types (decimal placed numbers), where the DS integer type is equivalent to a 64 bit signed integer and the double is equivalent to the standard C# double precision data type. Along with these types DS also implements comparison operators that operate on both number types; namely, equality and relational operators. These can be listed as the following:
= (equals, not equals)
<, >, <=, >= (greater than, less than, greater than or equal to, less than or equal to)
Other than serving their standard purpose of making comparisons between number operands, these operators are implemented specially in DS. They are implemented as functions in associative code, which means that wherever these operators are used (outside of loops or if-else conditional statements), they can replicate over their inputs. For example, if inputs ‘x’ and ‘y’ are lists of numbers:
x = [1, 2, 3, 4];
y = [2, 4, 1, 3];
The expression, x < y will result in a function call which will be called repeatedly over each element in the input lists, to produce the following output:
[true, true, false, false]
This is true with all other numeric and math operators in DS; they are all treated as functions that can replicate over higher dimensional operands when used in associative code.
Other than the above deviation of the comparison operators from platform level operators in C# (on top of which DS has been written), DS also has custom implementations of the equality and relational operators.
Unlike integers, which are easy to see as being different or equal, floating point numbers may be different, equal, or “close enough.” Due to the nature of floating point numbers in computer science, real numbers cannot be represented accurately and thus no two floating point numbers can be compared accurately at arbitrary precision. The DS double data type essentially being a floating point type also needs a tolerance value for this definition of “close enough.” The following is the most obvious way to compare two floating-point values ‘u’ and ‘v’ for being close at a given absolute tolerance epsilon:
Equals(a, b) => abs(u - v) <= epsilon;
DS being a high-level, end-user scripting language, designed for non-CS professionals, it internally uses the same formula above instead of using standard platform equality offered in C#. For historical reasons, DesignScript used a value of 1e-5 for the value of epsilon. We found this value to work well for “most” end-user workflows; it wasn’t too tight (small) a value to be very restrictive for non-scientific applications and not too loose (large) to make users lose data precision. However, not all users found the equality checks in DS or Dynamo to give expected and consistent results. For example, comparisons such as the following would be confusing without explicitly stating that internally DesignScript uses a fixed tolerance value and what that value is:
a1 = 0.12345;
b1 = 0.12346;
a1 == b1; // true
whereas the following gives a different result:
a1 = 0.1234;
b1 = 0.12345;
a1 == b1; // false
Better late than never; we now wish to, at the least, make this internal detail about DesignScript obvious to users of Dynamo:
epsilon = 1e-5
by making it more explicit in our documentation and Dynamo users better informed by specifying what “close enough” means in Dynamo. In an ideal world however, we do not want to apply the same absolute tolerance for huge and tiny numbers, the reason being that this method, cannot account for “close enough” equality of two floats over their entire dynamic range. Consider the following example of two large numbers representing the size of a large building complex, a 100 meters in length, in millimeters:
big1 = 100.0e+3;
big2 = 100.00000001e+3;
These numbers for most practical purposes would be considered equal however upon using the epsilon value of 1e-5 in
abs(big1 - big2) <= epsilon, they will turn out to be unequal, which is not what most users would expect. At this scale, an epsilon value of 0.1 units (mm in this case) would probably suffice to tell the two buildings apart (100,000 and 100,000.1). So we can see that the choice of epsilon is not straightforward where one size does not fit all. However, for reasons of not breaking a history of Dynamo user data and workflows, with a fundamental language change such as this, we decided not to change the epsilon value at this time.
The inequality of floating point numbers on the other hand is easily established using relational operators, >, <, >= or <=, and therefore there should be no ambiguity about implementing these in a language like DS either. However, DS had custom implementations for these too. In addition to checking for platform inequality, it also checked for inequality using the above epsilon value. For example, the ‘>’ (greater than) function implementation looked something like this:
GreaterThan(a, b) => (a > b) && !Equals(a, b)
This led to some confusing edge cases with these comparisons. We have addressed this complexity by simply getting rid of these custom implementations and using the standard operators offered by the platform or compiler-level >, <, >= and <= operators in C#.
GreaterThan(a, b) => (a > b)
What does this change mean for users?
We would be lying to users if we said that this is not a breaking change but feel that fixing this interaction now will result in more accurate graphs moving forward. The change in behavior is illustrated in the graph below. As can be seen the new comparison is more accurate and would be as expected in most cases. With the older implementation, adding the tolerance-based equality check to the mix, the two numbers turn out to be unexpectedly equal as the numbers are slightly smaller than the implicit tolerance, epsilon that is set to 1e-5.
So yes, we cannot guarantee that removing the tolerance-based equality checks from DS comparison operators will not affect existing user graphs, workflows, and packages, however we do know that these changes will be more consistent and better aligned with user expectations and therefore benefit users. We anticipate minimal impact to user workflows and if any, there will only be edge cases such as above that might be affected. The Dynamo team believes that it was best to fix the behavior as per best language–design practices sooner than later in order to prevent further accumulation of poorly constructed graph logic based on the previous comparison behavior.