7 ms·
Can you think of a function where the input is valid, the output is NaN and nothing has gone wrong in the process? I can't think of any, haven't experienced an
by pinteresting 5y ago
Can you think of a function where the input is valid, the output is NaN and nothing has gone wrong in the process?
I can't think of any, haven't experienced any, not heard of any examples of it, so you're welcome to break my ignorance on the subject.
- ImprobableTruth 5y agoDo you count under/overflow resulting in Inf as 'something has gone wrong'? If so, why would gracefully recovering be hard?
- vanviegen 5y agoBecause you're about 15 stack frames deep into some calculation when the problem gets noticed. So how do you handle this? You could throw an exception (or similar), which would allow the UI to state something semi-helpful like 'Calculation failed'. But recover? Usually not. I think NaN and inf can be really useful in specific cases, but they probably shouldn't be allowed to propagate through your program, and shouldn't occur unless you're specifically making use of them.
- adrian_b 5y agoWhen you do not want such values to be propagated, it is always possible to choose to have overflow exceptions instead of infinities and undefined operation exceptions instead of NaNs. Also underflow exceptions instead of denormals. The programmer should choose which is most appropriate for an application. Choosing to both ignore the exceptions and not generate a special value that can be examined later, that is seldom acceptable.
- jleahy 5y agoCalculating the average value for some observation in a time bucket, where some buckets may have no observation (resulting in 0/0=nan), finally taking some kind of summary of these ignoring nan values (there is a whole library of functions for this in numpy for example). NaN and Inf are extremely valuable and useful encodings to represent real things. Particularly signed infinity is wonderful, as functions like exp behave correctly for inf (eg. exp(-inf)=0).
- noctune 5y ago>Calculating the average value for some observation in a time bucket, where some buckets may have no observation (resulting in 0/0=nan) But there are many ways such a calculation could result in NaN besides a division. For example, intermediate sums could result in something like inf - inf. I find it rare that you can definitely conclude that a NaN means some specific thing.
- jacobolus 5y agoThis depends how you define “gone wrong”. In evaluating rational functions (a useful tool for approximating all sorts of other functions), one efficient and well behaved algorithm is the “barycentric formula”, based on interpolating function values at various specific input points. When the input is one of those points directly, this formula results in Inf / Inf = NaN. Evaluation code needs to check for this case, and then replace the output by the appropriate value for the input. ±Inf is a very natural output for many kinds of numerical codes.
- jlokier 5y ago> When the input is one of those points directly, That's against one of the rules of well-behaved floating point programming: Never test for equality.
- adrian_b 5y agoLike any rule, this has exceptions. You should never test for equality numbers which are known only approximately, which is the case for most FP numbers. There are nonetheless cases when certain FP values are known exactly and it is OK to test them for equality. In general the rule does not depend on number representation, it applies equally to floating-point numbers, fixed-point numbers, rational numbers and even large integers in some cases. What counts is whether a number is known exactly or only approximately.
- jlokier 5y agoThere are exceptions. But the type of barycentric interpolation formula that leads to Inf/Inf at an interpolation point isn't one of those exceptions - it's an example which shows why the rule should be borne in mind. When the input is very close to one of the interpolation points, the calculation becomes ill-conditioned, unsurprisingly as two values are diverging towards Inf, so the result can become incorrect as the input approaches the interpolation point, until it becomes Inf/Inf = Nan at some even closer, but non-zero, distance. Before reaching Nan it can also have an interval where the result is +/-Inf. It depends on the interpolation points. But under some circumstances, it is necessary to recognise when the input is close to an interpolation point, and adjust the formula appropriately.
- ChrisLomont 5y agoThere are algorithms that behave correctly but require intermediate Inf or NaN to work. If I recall there is one related to finding bounding box intersections in ray tracing. I know when I introduce such code into a codebase I put a large "DO NOT CHANGE THIS..." style comment with an explanation so some enterprising programmer doesn't put some if statements in to remove what they think is an error, when it is not. I'd suspect the same is true for NaN. These things were introduced for reasons. If you don't have a deep knowledge of why, don't assume everyone else is the same. Edit: I found examples using a NaN and Inf intermediates to get a large speed increase [1,2]. [1] https://tavianator.com/2015/ray_box_nan.html https://tavianator.com/2015/ray_box_nan.html [2] https://tavianator.com/2011/ray_box.html https://tavianator.com/2011/ray_box.html
- dkersten 5y agoSure, of course they have uses otherwise why would they exist. But I would argue that the examples you have aren’t the common case — most people’s floating point calculations do not rely on these special values and seeing them denotes something went wrong.
- ChrisLomont 5y agoOf course they're not the common case, but the parent wanted examples where they are used in calculations that do have nice answers. So they are useful. They are more useful than most people realize that simply assume they only mark errors. And without them in your case, you would not be alerted that a calculation went wrong. So even there they are very useful. Not using them correctly is like ignoring file API errors in your programs - sure they are rare, but you need to understand and handle them if you want to make good software.
- dkersten 5y agoAh right, fair enough! They are, indeed, good examples of the usefulness.
- MaulingMonkey 5y agoJavaScript's parseInt and parseFloat return NaN on something as benign as "well, that wasn't a number". var jsonish = "..."; var number = parseFloat(jsonish); if (!isNaN(number)) { // deserialized a number } else { // ...try deserializing as a boolean or string next... } Fast math optimizations can break code like this by breaking isNaN. I was porting a C++ project to a certain platform - and that platform enabled a -ffast-math equivalent by default in Release (but not Debug) builds! This broke duktape, a JS engine said project embedded, in some nasty and subtle ways. Instead of storing a number/pointer/??? (8 bytes) + type tag (4? bytes) for each dynamically typed JS value, duktape can bit-pack values into a single 8 byte "double" value by storing object/string handles as NaN values - this isn't an uncommon trick for dynamically typed scripting stuff: https://github.com/svaarala/duktape/blob/c3722054ea4a4e50f485516c70167e323636d9db/src-input/duk_tval.h https://github.com/svaarala/duktape/blob/c3722054ea4a4e50f48... Naturally, the -ffast-math equivalent broke isNaN checks, which caused random object/string handles to be mistakenly reinterpreted as "numbers" - but only in Release builds, for this one particular platform, in one rarely taken branch, so neither QA nor CI caught it, leading to hours of manufacturing a repro case, stepping through an absurd amount of code, and then finally looking at the default build rules and facepalming. Cursing the platform vendor under my breath, I overrode the defaults to align with the defaults of every other config x platform combination we already had: no fast math. If you want those optimizations, use SSE-friendly NaN-avoiding intrinsics - or, if you must use the compiler flags, ensure you do so consistently across build configs and platforms, perhaps limited to a few TUs or modules if possible. This allows you to have a chance at using your Debug builds to debug the resulting "optimizations".