Math.Tan() near -Pi/2 wrong in .NET, right in Java

2019-06-28 07:52发布

I have an unit test failing on a Math.Tan(-PI/2) returning the wrong version in .NET.

The 'expected' value is taken from Wolfram online (using the spelled-out constant for -Pi/2). See for yourselves here.

As correctly observed in the comments, the mathematical result of tan(-pi/2) is infinity. However, the constant Math.PI does not perfectly represent PI, so this is a 'near the limit' input.

Here's the code.

double MINUS_HALF_PI = -1.570796326794896557998981734272d;
Console.WriteLine(MINUS_HALF_PI == -Math.PI/2); //just checking...

double tan = Math.Tan(MINUS_HALF_PI);
Console.WriteLine("DotNET  {0:E20}", tan);

double expected = -1.633123935319534506380133589474e16;
Console.WriteLine("Wolfram {0:E20}", expected);

double off = Math.Abs(tan-expected);
Console.WriteLine("         {0:E20}", off);

This is what gets printed:

True
DotNET  -1.63317787283838440000E+016
Wolfram -1.63312393531953460000E+016
         5.39375188498000000000E+011

I thought it's an issue of floating-point representation.

Strangely though, the same thing in Java DOES return the same value as Wolfram, down to the last digit - see it evaluated in Eclipse. (The expressions are cropped - you'll have to believe me they use the same constant as MINUS_HALF_PI above.)

enter image description here

True
DotNET  -1.63317787283838440000E+016
Wolfram -1.63312393531953460000E+016
Java    -1.63312393531953700000E+016

As you can see, the difference is:

  • between Wolfram and .NET: ~5.39 * 10^11
  • between Wolfram and Java: =2.40 * 10^1

That's ten orders of magnitude!

So, any ideas why the .NET and Java implementations differ so much? I would expect them both to just defer the actual computing to the processor. Is this assumption unrealistic for x86?

Update

As requested, I tried running in Java with strictfp. No change:

enter image description here

1条回答
做自己的国王
2楼-- · 2019-06-28 08:36

The entire question is constructed to create a tendentious result. The double value closest to half PI is -1.5707963267948966; the other digits are just ignored. So it’s no wonder that neither C# nor Java detect that the remaining 14 more digits are not turning the result closer to -PI/2, but carefully chosen to trick Wolfram Alpha to return a value close to the result of Java.

-1.570796326794896557998981734272 // the number from the question
-1.57079632679489661923132169163975… // the real digits of -PI/2
                  ↑
                the end of the double precision

Any other number within the range that would get rounded to the same double number including the exact double value as used by Java yields to a value on Wolfram Alpha having nothing in common with neither, the C# nor Java result.

查看更多
登录 后发表回答