Bug 15802 - -[Single|Double].Epsilon equals Zero on devices
Summary: -[Single|Double].Epsilon equals Zero on devices
Status: RESOLVED NOT_ON_ROADMAP
Alias: None
Product: iOS
Classification: Xamarin
Component: XI runtime ()
Version: 7.0.4.x
Hardware: PC Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Zoltan Varga
URL:
: 24808 ()
Depends on:
Blocks:
 
Reported: 2013-10-29 18:06 UTC by Sebastien Pouliot
Modified: 2016-03-08 16:56 UTC (History)
4 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED NOT_ON_ROADMAP

Description Sebastien Pouliot 2013-10-29 18:06:38 UTC
This is similar to bug #15630 where the same tests fails using XS debugger (e.g. in the immediate pad).

This *works* when using the i386 JIT for the iOS simulator.

This *fails* when using the (default, non-LLVM) ARMv7 AOT compiler in Debug. LLVM and other options to be confirmed.
Comment 1 Sebastien Pouliot 2013-10-29 18:09:59 UTC
Test case committed on master be3ed2f7e8f0c8d697ffacac863aaaa4b91d7a6c
by default it won't execute on device (needs to comment that check to duplicate the issue)
Comment 2 Sebastien Pouliot 2013-10-29 18:18:28 UTC
ARMv7 (debug): fails
ARMv7 LLVM (release): fails
ARMv7 Thumb LLVM (release): fails

I have not looked at ARMv6 or ARMV7s.
Comment 3 Zoltan Varga 2013-10-30 08:15:01 UTC
This seems to be an ARM hardware quirk, see the 'Platform Notes' section here:
http://msdn.microsoft.com/en-us/library/system.single.epsilon.aspx
Comment 4 Sebastien Pouliot 2013-10-30 08:27:10 UTC
Interesting but [Single|Double].Epsilon *do* works for us. It's the negative of the values, e.g. `-Single.Epsilon`, that equals 0 (still it could be the same reason, maybe we're special casing the positive Epsilon somewhere ?).

I wonder what WinRT device reports for Epsilon and if we should match that ?!?
Comment 5 Sebastien Pouliot 2013-10-30 08:34:36 UTC
There's another note about:

<quote>The value of the Epsilon property is not equivalent to machine epsilon, which represents the upper bound of the relative error due to rounding in floating-point arithmetic.</quote>

so I expect WinRT has the same (.NET) constant values even if they are not really usable.

Not sure it needs/can be fixed, beside being documented on our side, but I'm still curious about the positive/negative differences.
Comment 6 Tom Spilman 2013-10-30 10:57:59 UTC
> so I expect WinRT has the same (.NET) constant 
> values even if they are not really usable.

We have the exact code working on Windows Phone 8 and Windows 8 Store apps that run on ARM devices.  The only place the code did not function is on iOS.

So regardless of what the MSDN docs say, this does work on ARM devices on WinRT.
Comment 7 Zoltan Varga 2013-10-30 12:22:03 UTC
What code fails exactly ?

I tried
Console.WriteLine (Single.Epsilon == 0.0f);
Console.WriteLine (-Single.Epsilon == 0.0f);
Console.WriteLine (Single.Epsilon.Equals (0.0f));
Console.WriteLine ((-Single.Epsilon).Equals (0.0f));

and they all print false.

I enabled the tests in LinkAllTest.cs, and they pass too.
Comment 8 Sebastien Pouliot 2013-10-30 12:53:36 UTC
Interesting it works on my iPhone5S - but it fails on my iPod Touch 5th gen (armv7).

2013-10-30 12:52:32.762 linkall[759:60b] 	[FAIL] SingleEpsilon :   Epsilon
  Expected: not 0.0f
  But was:  0.0f
2013-10-30 12:52:32.763 linkall[759:60b] 		  at MonoTouchFixtures.LinkAllRegressionTest.SingleEpsilon () [0x00024] in /Developer/MonoTouch/Source/monotouch/tests/linkall/LinkAllTest.cs:299 
2013-10-30 12:52:32.765 linkall[759:60b] 		  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
2013-10-30 12:52:32.767 linkall[759:60b] 		  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00044] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Reflection/MonoMethod.cs:230 


2013-10-30 12:52:32.631 linkall[759:60b] 	[FAIL] DoubleEpsilon :   Epsilon
  Expected: not 0.0f
  But was:  4.9406564584124654E-324d
2013-10-30 12:52:32.633 linkall[759:60b] 		  at MonoTouchFixtures.LinkAllRegressionTest.DoubleEpsilon () [0x00028] in /Developer/MonoTouch/Source/monotouch/tests/linkall/LinkAllTest.cs:311 
2013-10-30 12:52:32.635 linkall[759:60b] 		  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
2013-10-30 12:52:32.637 linkall[759:60b] 		  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00044] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Reflection/MonoMethod.cs:230
Comment 9 Tom Spilman 2013-10-30 13:37:27 UTC
So specifically my original code was for a timer:

_time = Math.Max(_time - seconds, -Single.Epsilon);

The expected behavior was for the timer to stop when it was a little less than zero.  On a Windows 8 Store game on ARM and x86 as well as Windows Phone game on ARM this code worked and _time would become less than zero.  This code works regardless of what the MSDN docs say about ARM and Single.Epsilon.

It was on iOS where we noticed the timer was not working correctly and we found that _time was clamped to 0.  Which was caused by -Single.Epsilon evaluating to 0 and not a small negative value.

We fix this temporarily by using our own constant of -0.0001f or something.  The actual value didn't matter to us other than it was < 0.  

Still it is a bug in the expected behavior for Single.Epsilon IMO.
Comment 10 Zoltan Varga 2013-10-30 15:08:19 UTC
I can repro on an ipad4, but not on an iphone5s. Will investigate.
Comment 11 Zoltan Varga 2013-10-30 16:06:57 UTC
So this is definitely a hw difference. We are executing these 4 instructions:

0x51460:  0xeeb42b43   vcmp.f64 d2, d3
0x51464:  0xeef1fa10   vmrs   APSR_nzcv, fpscr
0x51468:  0x13a00000   movne  r0, #0
0x5146c:  0x03a00001   moveq  r0, #1

Here d2 is epsilon (0x1), and d3 is 0.0f (0x0). After this, r0 is set to 1 on an ipad4, and 0 on an iphone5s.
Comment 12 Sebastien Pouliot 2013-10-30 20:12:17 UTC
So different ARM CPU compare* epsilon differently with 0f ? fascinating

* compare or process ? that would explain the 2nd part of the test (which also works on the 5S).

2013-10-30 20:06:15.701 linkall[871:60b] 	[FAIL] SingleEpsilon :   Epsilon.ToString()
  Expected string length 12 but was 1. Strings differ at index 0.
  Expected: "1.401298E-45"
  But was:  "0"
  -----------^

note: that diverge more (since it affects Epsilon, not just -Epsilon) from bug #15630 affecting the debugger (watch and immediate pads)
Comment 13 Sebastien Pouliot 2013-10-30 20:29:52 UTC
It turns out that Double.Epsilon.ToString() (and the negative version) works on my iPodTouch 5th gen (but Single does not). I split the test cases to make it easier to spot.

Anyway it does explain the MSDN warning - why it's there and why it's not always true (it probably was when written, e.g. .NET Compact Framework era).
Comment 14 Tom Spilman 2013-10-30 20:35:40 UTC
I really don't know the rules to all this, but IMO the value of Single.Epsilon doesn't matter as long as it is the smallest representable value both positive and negative.  This seems like a better default behavior than returning zero.
Comment 15 Zoltan Varga 2013-10-30 21:09:51 UTC
So as the MSDN documentation says, the value of Epsilon might not be distinguishable from zero. You should use some small constants like 0.00001 instead.
Comment 16 Tom Spilman 2013-10-30 21:19:31 UTC
> So as the MSDN documentation says, the value of 
> Epsilon might not be distinguishable from zero.

So basically... never use Single/Double.Epsilon as it is not a dependable mathematical epsilon as the name and documentation imply.
Comment 17 Zoltan Varga 2014-11-18 19:32:20 UTC
=> WONTFIX.
Comment 18 Rolf Bjarne Kvinge [MSFT] 2014-11-25 11:38:42 UTC
*** Bug 24808 has been marked as a duplicate of this bug. ***
Comment 19 Zoltan Varga 2016-03-08 16:56:00 UTC
*** Bug 39443 has been marked as a duplicate of this bug. ***