2

I need to add a very small value to a floating point value to make it insignificantly different so that it fails an equality test.

To avoid issues with precision, instead of adding a very small number, I have opted to add a relatively small number. Is this a good solution? or is there a reliable way to add an even smaller number?

matrix.m00 += matrix.m00 * 0.0000001f;
matrix.m11 += matrix.m11 * 0.0000001f;
matrix.m22 += matrix.m22 * 0.0000001f;

From reading I have found that the best solution is to use the next representable floating point number. Though in C# the process of doing this either a) requires unmanaged/unsafe code, or b) uses BitConverter which is too slow. So I figured that the above solution would work, but I would like to know if there are any gotchyas.

Lea Hayes
  • 62,536
  • 16
  • 62
  • 111

3 Answers3

3

You can add an ulp to any double (depends on the double); that is the smallest number that you can add or subtract to it that will change its value.

Though, those posts all use BitConverter. I discovered a post that discusses how to add an ulp without unsafe code or BitConverter, though:

http://realtimemadness.blogspot.com/2012/06/nextafter-in-c-without-allocations-of.html

Community
  • 1
  • 1
Andrew Mao
  • 35,740
  • 23
  • 143
  • 224
  • 1
    Indeed. But the OP is essentially asking how to calculate that. – Oliver Charlesworth Feb 23 '13 at 01:28
  • The last link in your answer, I did come across that when searching, but I do not understand it. For example, why does `u.i++;` and `u.i--;` make any difference at the end of the function? – Lea Hayes Feb 23 '13 at 01:34
  • @Lea Do you know what a `union` in C is? This is an attempt to 'simulate' this. Since both member variables are `FieldOffset(0)`, they both refer to the same point in memory. Some kind of pointer aliasing under the covers. – us2012 Feb 23 '13 at 01:35
  • Ah, so `i` and `f` are addressing the same field bits? – Lea Hayes Feb 23 '13 at 01:37
  • 1
    @Lea Yes, that's essentially what's happening. – us2012 Feb 23 '13 at 01:39
  • Do you think that there will be issues on mobile platforms with this approach? I guess this is probably a different question, but would it be safe to assume that Mono and .NET both use the IEEE 754 standard? – Lea Hayes Feb 23 '13 at 01:46
  • @Lea I would think it should work on mobile platforms (but I admittedly don't know). However, it seems to me that this kind of operation belongs in the domain of numerical/scientific simulations, so I'm not quite sure what its purpose would be on a phone? – us2012 Feb 23 '13 at 01:54
  • In this particular circumstance it is to workaround an issue with the framework that I am using. When the value has not changed an undesirable side-effect occurs. However simply adding a very small amount indicates to the framework that the value is to be treated anew. – Lea Hayes Feb 23 '13 at 02:00
1

Sure there's a gotcha. If any of these values is 0, then you'll be adding exactly 0, i.e. not modifying the value at all.

Is there any reason why you couldn't use unsafe code to do this?

Asik
  • 21,506
  • 6
  • 72
  • 131
  • Thanks for pointing out the case with 0, thankfully 0 is the only invalid value (i.e. it is not ever a value in my code). Unsafe code is not supported by the platform that I am using. – Lea Hayes Feb 23 '13 at 01:32
  • 1
    @LeaHayes There exist IEEE 754 floating-point numbers `n` other than +0.0 and -0.0 that have the property that `n + n * 0.0000001f == n`. – Pascal Cuoq Feb 23 '13 at 15:02
1

The minimum number you can add to a floating point number such that a different number is produced is a function of the original number, it's not some constant. Call this function Epsilon(x).

Epsilon(0), i.e. the minimum floating point number you can add to floating point 0 such that a distinguishable number is produced, can be found in the static value Double.Epsilon.

Even using a "large" epsilon like 1 will eventually fail, though. For example, this returns true in C#:

var big = 10000000000000000.0;
Console.WriteLine(big == (big + 1.0));

So unless you are sure that your input is in some fixed range of magnitude (e.g. all close to 0), you can't just fudge it with a single constant.

latkin
  • 16,402
  • 1
  • 47
  • 62