I imagine this project is inspired by the sad state of numerical computing in Javascript, but this proposal will surely only make it worse. The world certainly doesn't need a new, incompatible, poorly-thought-out floating point format.
Compare the level of thought and detail in this "specification" to the level of thought and detail in this famous summary overview of floating point issues: https://ece.uwaterloo.ca/~dwharder/NumericalAnalysis/02Numer... ("What every computer scientist should know...")
> DEC64 is intended to be the only number type in the next generation of application programming languages.
Jesus, I certainly hope not.
In fact, the only line of actual substance in your post is "For example, rounding modes and overflow behavior are not addressed", and it turns out that's only true for the descriptive web page, not the reference implementation.
> I imagine this project is inspired by the sad state of numerical computing in Javascript, but this proposal will surely only make it worse. The world certainly doesn't need a new, incompatible, poorly-thought-out floating point format.
I'll just quote pg here:
Yeah, we know that. But is that the most interesting thing one can say about this article? Is it not at least a source of ideas for things to investigate further?
The problem with the middlebrow dismissal is that it's a magnet for upvotes. The "U R a fag"s get downvoted and end up at the bottom of the page where they cause little trouble. But this sort of comment rises to the top. Things have now gotten to the stage where I flinch slightly as I click on the "comments" link, bracing myself for the dismissive comment I know will be waiting for me at the top of the page.
My gut sense is that this proposal is simply too amateurish to be worth the effort of thoroughly debunking. Floating point is kind of like cryptography: the pitfalls are subtle and the consequences of getting it wrong are severe (rockets crashing, etc). Leave it to the experts. This is not a domain where you want to "roll your own."
He provides a reference implementation. That means this and many other details are defined by code. Quoting dec64.asm: "Rounding is to the nearest value. Ties are rounded away from zero. Integer division is floored."
> Compare the level of thought and detail in this "specification" to ... [Goldberg]
I don't think this comparison is fair. David Goldberg's text is an introduction to the topic. Douglas Crockford describes an idea and gives you a reference implementation.
To be fair it is extensively commented, but the comments describe what it does, not why. And for fuck's sake, hundreds of lines of assembly is not a spec, even if it is most readable code in the world
I dream of a day when we stop thinking of "reference implementations" as proper specifications. The whole concept of a "reference implementation" leads to an entire class of nightmarish problems.
What happens if there is an unintentional bug in the reference implementation that causes valid (but incorrect) output for certain inputs? What if feeding it certain input reliably produces a segfault? Does that mean that other implementations should mimic that behavior? What if a patch is issued to the reference implementation that changes the behavior of certain inputs? Is this the same as issuing an amendment to the specification? Does this mean that other implementations now need to change their behavior as well?
Or, worse, what if there is certain behavior that should be explicitly left undefined in a proper specification? A reference implementation cannot express this concept - by definition, everything is defined based on the output that the reference implementation provides.
Finally, there's the fact that it takes time and effort to produce a proper specification, and this process usually reveals (to the author) complexities about the problem and edge cases that may not become apparent simply by providing a reference implementation.
I don't agree with that, and I don't think BASIC has much (if anything) to offer in terms of good language design.
From http://bitsavers.trailing-edge.com/pdf/dartmouth/BASIC_Oct64..., that appears to be the case.
Off topic: in that PDF (page 4) the letter "Oh" is distinguished from the numeral "Zero" by having a diagonal slash through the"Oh". Yes, that program printed "NØ UNIQUE SØLUTIØN".
That made me think of the periodic rants here on HN about the supposedly neigh insurmountable inconsistencies in mathematical notation.
Why? A similar thing is being used by some JavaScript and Lua implementations. It's called NaN boxing.
> nan is also the result of operations that produce results that are too large to be represented.
It's a polemic, designed to try and propagate an idea and change minds.
It didn't change my mind much, but I found your comment more affecting - in the humorous!
> There are 255 possible representations of zero. They are all considered to be equal.
There are also 255 representations of almost all representable numbers. For example, 10 is 1 x 10^1 or 10 x 10^0 – or any one of 253 other representations. Aside from the fact that you're wasting an entire byte of your representation, this means that you can't check for equality by comparing bits. Take a look at the the assembly implementation of equality checking:
https://github.com/douglascrockford/DEC64/blob/master/dec64....
The "fast path" (which is ten instruction) applies only if the two numbers have the same exponent. The slow path calls subtraction and returns true if the result is zero. The implementation of subtraction falls back on yet another function, which jumps around even more:
https://github.com/douglascrockford/DEC64/blob/master/dec64....
For most comparisons (no, comparing numbers with the same exponent is not the norm) it will take around FIFTY INSTRUCTIONS TO CHECK IF TWO NUMBERS ARE EQUAL OR NOT. Many of these instructions are branches – and inherently unpredictable ones at that, which means that pipeline stalls will be normal. All told, I would expect equality comparison to typically take around 100 cycles. It's not even clear to me that this implementation is correct because at the end of the subtraction, it compares the result to the zero word, which is only one of the 255 possible representations of zero. The lack of a single canonical representation of any number is just as bad for other arithmetic operations and comparisons, if not worse.
Crockfords bugaboo with IEEE 754 floating-point is bizarre, verging on pathological. He devoted a section in his book "JavaScript: The Good Parts" to a rather ill-informed rant against it. When I saw him give a talk, I took the opportunity to ask him what he thought would be a good alternative to using IEEE 754. His answer was – I shit you not – "I don't know". Apparently this proposal is the answer. No thanks, I will stick with the amazingly successful, ubiquitous, thoroughly thought out standard, that was spearheaded by William Kahan – one of the greatest numerical analysts of all time. Anyone who doesn't appreciate how good we have it with IEEE 754 should really read "An Interview with the Old Man of Floating-Point" [1], in which Kahan relates just how messed up this stuff was before the IEEE 754 standardization process. It should also be noted that there already is an IEEE standard for decimal floating-point [2], which is not only infinitely better thought out than this drivel, but also is already implemented in hardware on many systems sold by IBM and others, specifically for financial applications.
[1] http://www.cs.berkeley.edu/~wkahan/ieee754status/754story.ht...
[2] http://en.wikipedia.org/wiki/Decimal64_floating-point_format
> There are also 255 representations of almost all representable numbers. [...] Aside from the fact that you're wasting an entire byte of your representation
How is this different than any other floating-point representation? I'm pretty sure IEEE floating-point has the same redundancy, though numbers are normalized so comparisons are cheaper as you note. But IEEE doubles "waste" even more bits due to the 2^52 representations of NaN.
> For most comparisons [...] it will take around FIFTY INSTRUCTIONS TO CHECK IF TWO NUMBERS ARE EQUAL OR NOT.
Good point, sounds like a notable weakness and barrier to adoption.
> Crockfords bugaboo with IEEE 754 floating-point is bizarre, verging on pathological.
He calls it "the most frequently reported bug in JavaScript." Wouldn't you be interested in improving on the most common cause of user confusion in a technology you care about?
I personally think that the way to handle floating-point confusion is better user education. However, if you really want a decimal standard, then, as I mentioned above, there already is one that is part of the IEEE 754 standard. Not only do there exist hardware implementations, but there are also high-quality software implementations.
A better approach to making things more intuitive in all bases, not just base 10, is using rational numbers. The natural way is to use reduced paris of integers, but this is unfortunately quite prone to overflow. You can improve that by using reduced ratios of – guess what – floating point numbers.
Nobody who does anything with numbers believes that! Even if all you can do is count your fingers you believe in the difference between integers and floats. They have different algebraic properties entirely and it takes a whole hell of a lot of work to get from one to the other---there's even a whole class (fractions) in between.
This isn't true. Only zero has 255 representations. All other numbers are limited by the size of the coefficient, 16 digits. So a number with 1 significant digit would have 16 representations, one with two would have 15, etc. Not that I'm defending the format, but if we're critiquing it for being sloppy, we need to be careful about such things.
> For most comparisons (no, comparing numbers with the same exponent is not the norm)
I'd say that exponent 0 is the norm, meaning precise numbers in a really wide range, isn't it?
What I meant by exponent zero not being the norm is that most numbers you work with won't have exponent zero. Specifically only one in 2^16 of the possibly representable values and only one in 2^24 of the possible representations. If you're using the "natural" representation, then only the integers -9 through 9 have zero exponent.
Of course ... That's true of any decimal number representation with an exponent. Why is it a problem?
JSON is a subset of javascript which already existed. He just coined the phrases 'json' and wrote it down.
First, because some people will treat that authorship credit as a warning label rather than authority; that would make it ad hominem, not appeal to authority.
Second, because it actually gives useful context to some parts of this spec: "Oh, it makes sense that the author of a language without integer types would propose a spec that claims you don't need integer types". That's not a logical fallacy at all; that's perfectly reasonable reasoning.
I don't think the person you replied to was claiming that DEC64 was good or bad because it was written by Crockford? Didn't feel like he was passing any judgement, merely pointing out who the author was...
Also, see: http://www.logicalfallacies.info/relevance/fallacists/
What! I do not see how making a float fast on integers should ever eliminate the need for an int type. Ints and Real-approximations like Dec64 are simply different things entirely.
It's annoying enough that this happens in Javascript.
As a C programmer, he just seems off his trolley.
What is "int truncation" anyway? Overflow?
EDIT: not a javascript specific thing, rather a float one, methinks
If both base2 and base10 floating point are implemented in hardware, what makes base10 inherently less efficient?
Also, I don't have a good intuition for the difference in what numbers can be exactly represented. I'd love to see this represented visually somehow.
Double precision can exactly represent integers up to 2^53, then half of integers between 2^53 and 2^54, then a quarter of integers between 2^54 and 2^55, etc.
Dec64 would be able to exactly represent integers up to 2^55, then 1/10th of all integers between 2^55 and 10(2^55), then 1/100th of all integers between 10(2^55) and 100(2^55).
So the "holes" are different, so-to-speak. How would this affect accuracy of complicated expressions?
Binary floating point can accurately represent fractions of the form 1/(2^n). Decimal floating point can accurately represent fractions of the form 1/((2^n)*(5^m)). Either can only approximate fractions with other prime factors in the denominator (1/3, 1/7, 1/11, ...).
In terms of a programmer having to be concerned with accumulated errors due to approximations in the representation, I'd assert that decimal floating point in no way changes the scope of concerns or approach to numeric programming compared to binary floating point. I'd guess even in a domain with a very precise need of fractional decimals, a programmer would still need to reason about and account for numeric representation errors in decimal floating point such as overflow and underflow.
The larger the radix, the more of the mantissa is wasted. Given fixed storage, base 2 floats will have higher precision over their range than higher bases, like 10 and 16.
The difference is easy to illustrate with base 16 and base 2, since we can convert between the two easily. Converting a base 16 float to base 2 will result in leading zeroes some of the time, which could have been used for storing data. The same is true with base 10, but you have to do more math to demonstrate it.
Agree. What problem does this solve? That I can now represent 1/5 precisely?
All the other problems of representing floating point numbers and numbers generally are maintained.
For example, arithmetic operations in binary floating point are notorious for producing mystery meat error quantities, because the actual error quantity is in binary and only later represented as a decimal amount. And when coding it is most natural to account for floating point errors in decimal terms, even though this is a false representation.
So the main thrust of any decimal float proposal comes from "this is more in tune with the way humans think" and not the specific performance and precision constraints.That said, I have no special insights into Crockford's proposal.
Next week he'll have a number format with 48 mantissa bits, 8 exponent bits and 8 unsigned base bits to define a base value between 0 and 255. Look at all the performance and simplicity involved!
Why? Why aren't you use half-bytes also?
If all your pointers are 64-bit aligned, all your variables are 64-bit aligned and your processor isn't any faster processing 16-bit numbers - if it even have instructions to process those - than 64-bit numbers?
I actually use half-bytes when it makes sense; my language of choice has bit-vectors so I can use exactly the number of bits I desire.
> If all your pointers are 64-bit aligned, all your variables are 64-bit aligned and your processor isn't any faster processing 16-bit numbers - if it even have instructions to process those - than 64-bit numbers?
Maybe I have an array with at least 4 16-bit numbers? If I'm counting bits, then it already means I have a lot of numbers. If I have 2 billion numbers in the range [0,15] Then I can easily represent them in an array of 4 or 8 bit values, but will run into performance issues trying to do so (if I can at all) using a similar array of 64 bit values.
Memory bandwidth. If the processor can read a single 64-bit integer in a clock cycle, it can read 4 16-bit ones just as well. Memory is slower than the core.
As an example with AVX instructions you can process 8 floats at the same time, compared to 4 doubles. So if float is enough for you you can expect double performance in either memory transfer bound or ideally vectorizable algorithms.
And in mobile computer graphics 16bit values are common.
- A value is either a 63 bit signed integer or a pointer to external storage, using one bit to tell which.
- One instruction to take two operands, test if either is a pointer, do arithmetic, and test for overflow. Those cases would jump to a previously configured operation table for software helpers.
- A bit to distinguish bigint from trap-on-overflow, which would differ only in what the software helper does.
- Separate branch predictor for this and normal branches?
I don't know much about CPUs, but this doesn't seem unreasonable, and it could eliminate classes of software errors.
The way modern, heavily-pipelined processors are designed, any instruction that could possibly need to obtain an address from an address and jump to there would have to be micro-coded. Also, from the instruction dependency point of view, the way modern, heavily-pipelined processors are designed, all instructions dependencies are fetched as early as possible (before the instruction is executing and it is known which may be ignored). This is why cmov is sometimes worse than a conditional branch. The entries of the “previously configured operation table” would need to be fetched all the time. Again, the simplest way not to fetch them all the time would be to micro-code the instruction, meaning that it would take about as much time as a short sequence of instructions that expands to the same micro-instructions.
There used to be a way in IA-32/x86_64 to annotate conditional branch instructions with a static prediction (cs: and ds: prefixes), which could be useful regardless of the approach, but nowadays I believe this annotation is ignored altogether.
My main problem with writing it out is code size, since ideally you want every single integer operation in your nice fast C-like-language program to expand to that kind of sequence. But maybe it doesn't actually matter that much.
What you want is called software interrupt or exception. MIPS and Alpha can do it for integer overflows. SPARC can do it for the two bottom tag bits of integers (to distinguish integers from pointers). I bet that if you only wait long enough, you will eventually see an architecture with both features. ;-)
In the meantime we have to fall back to conditional jumps. :-/
In the bigger picture what i fell we are generally missing in most programing languages is an EASY way to store and handle fractions.
I'm squeamish about that. I fear that a lot of new programmers would overuse a fraction type if it were built in, and this can quickly lead to really poor performance.
In most cases, floating point works just fine. Reducing fractions is slow, so you really only want to use them when perfect precision is absolutely necessary.
What I see happening is someone new to programming hits a point where they need a fractional number type. They look in the documentation and they see "integer, floating point, fraction" and they say "Aha! Fraction! I know what those are."
(10/3)x(9/4) == 7.5
but due to the fact that computers store the value instead of the fraction it would come out as something like 7.4999999999 cause instead of doing (10x9)/(3x4) it would do 3.3333333333333 x 2.25
Please no. There's a very solid case for integers in programming languages: many places in code call for a number which must be an integer: having the type enforce this is nice: you don't need to check and abort (or floor, or whatever) if someone passes you a non-integer. Basically, anything that does an array index, which is any for loop walking through a memory buffer (which might be an array, a string, a file, the list goes on and on. Anything that can be counted) wants an integer. x[i] where i is not an integer just doesn't make sense: ideally, let a type system enforce that.
Granted, of course, that many languages will just truncate a float in an integers context, and so funny stuff does happen (I don't really feel that this is a good thing). (Although interestingly, JS is not one of them.) Personally, I think JS needs an integer type. Especially when you see people start getting into bignum stuff in JS, it gets silly fast, as first you can only store so many bits before floating point loses precision, but even then, even if you have an "integer", JS will find funny ways to shoot you in the foot. For example:
x | 0 === x
does not hold true for all integers in JS.Technically correct, but fails to address the core point that this might be a terrible idea.
Also, it's worth mentioning http://speleotrove.com/decimal/ as a great repository of decimal float info. I hope it will be updated with some discussion of this proposed format.
1 / 0 == 0 / 0 == (-1) / 0 == MAX_DEC64 + X == (-MAX_DEC64) - X
(X being the some number large enough to cause overflow)I'd qualify the proposal as a midbrow dismissial of all the real thought that went into the IEEE float standards (I mean, there is no actual substance to his criticism, except "uh, look, it's so bad nobody uses it," which isn't even true, it is in IBM Power7, IBM zSeries, Fujitsu SPARC64, SAP ABAP and gcc). Floating point on a finite precision computer is hard, you can't gloss over the details and assume you have solved everything by that.
However, to Doug Crockfords credit, the number space it covers is pretty useful for a lot of different things and in scripted languages and other uses that aren't safety related I can see the advantage of an 'fractional integer' type.
Edit: Mike Cowlishaw, who is also quite interested in decimal arithmetic has a much deeper treatment here: http://speleotrove.com/decimal/
The summary: RLIM_INFINITY on a 32-bit system can be safely stored in a double. RLIM_INFINITY on a 64-bit system can't. I had code in Lua (which uses doubles as the only numeric type) that worked fine on 32-bit systems, but not 64-bit systems. It took me two days to track the root cause down.
(Edit: added summary)
Nonsense. Binary floating-point too normalizes to the largest possible significand. It then omits the leading 1 from the representation, since the leading 1 is by definition a 1. That this trick is simple to pull in binary is only one of the advantages of a base-2 floating-point representation.
The fact that 0.1 is not representable in binary has nothing to do with the choice of representing it as 0x1999999999999a * 2^-56, because it already is, and that does not make it representable.
I really don't want to rehash the arguments that applied 30 years ago, as they do today. Decimal floating point is good for some things, but to be considered the "only number type in the next generation of programming languages" is laughable.
In any case, I don't think DEC64 is intended for those users that need to use cos() et al.
He apparently has other ideas: "DEC64 is intended to be the only number type in the next generation of application programming languages."
IBM put hardware support for the IEEE 754-2008 Decimal Format in their POWER architecture. The POWER7 clocks 5 GHz. Decimal floating point is only considered slow because Intel and ARM do not have any support for decimal floating point in hardware. Lack of acceptance probably comes from lack of support in standard libraries rather than inefficiency inherent in the standard.
Reference: https://en.wikipedia.org/wiki/POWER7
Anyhow, clock rate in this case is irrelevant. Look at the instruction latencies. I don't have these handy, but I'd bet $50 at at the chance of winning $10 that decimal floating point instructions (at least the divide) are slower than the IEEE754 ones.
These chips are also ridiculously expensive, so probably not the best benchmark.
Is he seriously arguing that integer types should be eliminated? What the hell?
I really hope for the author's sake this is a joke.
Douglas Crockford is the standardizer of JSON and author of "JavaScript: The Good Parts", in which he popularized non-terrible Javascript. Brendan Eich is the creator of Javascript.
However, one nit in terms of interesting architecture: The Burroughs 5000 series had a floating point format in which an exponent of zero allowed the mantissa to be treated as an ordinary integer. In fact, the whole addressing scheme was decimal. The addresses were stored in one decimal digit per nibble, so it was doing decimal at the hardware level.
While this looks interesting at first blush, good luck with DEC64 is intended to be the only number type in the next generation of application programming languages. I think float will be around for a while, what with the blinding speed in today's processors, and the availability of 80 bit intermediate precision.
...and well-understood numerical behavior...
> DEC64 is intended to be the only number type in the next generation of application programming languages.
> DEC64 is a number type. It can precisely represent decimal fractions with 16 decimal places, which makes it well suited to all applications that are concerned with money.
From the reference code
> Rounding is to the nearest value. Ties are rounded away from zero.
Useful wikipedia entry regarding rounding (http://en.wikipedia.org/wiki/Rounding#Round_half_to_even)
> This variant of the round-to-nearest method is also called unbiased rounding, convergent rounding, statistician's rounding, Dutch rounding, Gaussian rounding, odd-even rounding, bankers' rounding, broken rounding, or DDR rounding and is widely used in bookkeeping. This is the default rounding mode used in IEEE 754 computing functions and operators.
Rounding towards zero isn't compatible with financial calculations (unless you're performing office space style bank theft), so this should never be used for numeric calculations involving money. I wonder what he was really trying to solve with this since he missed a fairly important aspect of the big picture. That being said, there's no problem statement on his entire website to address what actual problem he was trying to solve, so all we see is a half baked solution for some unknown problem. On the plus side, at least he didn't use the "for Good, not Evil" clause in the license this time.
edit: formatting
In other cases, you need to actually think about rounding mode (e.g. how many shares of AAPL can I buy with $5000).
> there's no problem statement on his entire website to address what actual problem he was trying to solve
This is very true. I don't understand what problem this is trying to solve, because I don't spend a lot of time confused by the internal detail that a double is binary. The storage format isn't important beyond knowing roughly how many digits of accuracy I can count on.
FFS, just because the designers of JS couldn't numerically compute their way out of a paper bag doesn't mean that FUTURE languages should be saddled with that mistake.
http://speleotrove.com/decimal/decnumber.html
Have used this for a number of years for financial calculations.
The C and C++ standards committees have been drafting decimal support based on IEC 60559:2011 (previously IEEE 754-2008) since its creation.
Original C TR: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1312.pdf
Latest draft specification: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1781.pdf
Original C++ TR: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n284...
Update proposal for C++11: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n340...
GCC, ICC, IBM C, and HP C have adopted the extension. Links to the respective software implementations can be found under "cost of implementation" in the C++11 proposal above, except GCC, which uses IBM's library. Its support is documented here: http://gcc.gnu.org/onlinedocs/gcc/Decimal-Float.html
Meanwhile, hardware support so far exists in POWER6 and 7 and SPARC64 X.
This may seem like slow adoption if you aren't accustomed to the glacial pace of standards processes. Especially in the context of a component as critical as floating point numerics. If there is any holdup it would be lack of demand for this format, which the article's proposal doesn't affect.
Python's decimal type conforms to IBM's General Decimal Arithmetic Specification [2] which is based on IEEE 754 and IEEE 854. Python's decimal type is very complete. For example Python supports the complete range of rounding options found in these specifications (ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP, and ROUND_05UP). By comparison Dec64 supports only one.
Despite having used Python on and off for over 10 years, I've never felt a need to use this decimal type. Integers and floats seem to work for me (although it's nice to have a good decimal implementation available if I need it).
[1] http://docs.python.org/3.4/library/decimal.html [2] http://speleotrove.com/decimal/decarith.html
1) The exponent bits should be the higher order bits. Otherwise, this type breaks compatibility with existing comparator circuitry.
2) This representation uses 10 as the exponent base. That will require quite a bit of extra circuitry, as opposed to what would be required if a base of 2 was used. Citing examples from COBOL and BASIC as reasons for using base 10 is not a very convincing.
3) With both fields being 2's compliment, you're wasting a bit, just to indicate sign. The IEEE single precision floating point standard cleverly avoids this by implicitly subtracting 127 from the exponent value.
4) 255 possible representations of zero? Wat?
5) This type may be suitable for large numbers, but there's no fraction support. In a lot of work that would require doing math on the large numbers that this "standard" affords, those large numbers are involved in division operations, and there's no type for the results of such an operation to cast into.
6) This data type seems to be designed to make efficient by-hand (and perhaps by-software) translation into a human-readable string. But who cares? That's not a reason to choose a data type, especially not a "scientific" one. You choose data types to represent the range of data you have in a format that makes for efficient calculation and conversion with the mathematical or logical operations you want to be able to perform.
So I guess making sure that you can never do array[0.5] before your program ever runs doesn't matter? At least not to Crockford apparently, who seems to have some kind of irrational hatred for static type systems.
A: Because DEC 25 = OCT 31
And we're done here folks.
[1] http://stackoverflow.com/questions/1797806/parsing-a-hex-for...
I think people freaking out that they're taking our precious integers away are being a little brash. A natural number type could easily be built on top of a dec64 type for those cases where you really need only whole numbers.
Decimal floating point is actually a good, underutilized idea. However, there is a VERY good specification here: http://speleotrove.com/decimal/dbspec.html
It is written by people who understand the problem, and have thought quite deeply about the solutions.
As opposed to this pile of garbage ...
This is like saying that the sharp blades on scissors (diverse numeric types) make them prone to causing bodily harm to surrounding people(errors due to constraints of types), then concluding that we should replace all scissors (numeric types) with those rounded plastic scissors(dec64 play dough floats) which come with a play dough set.
Every time somebody has the idea that by deluding developers more we can save them trouble and make them feel safer, we pat that person on the back and follow their recipe. Then two years later there's a HN post about why you really shouldn't be doing whatever was prescribed.