3

During the conversion of C++ code to C# we noticed that the results of C++ sin() function are different from C# Math.Sin().

Why? What's the reason behind this?

Is it possible to implement a C# sine method that returns exactly the same values?

More details here.

EDIT: These are some angles which show incorrect results passing C++ std::nextafter() values to C# Math.Sin():

1. Angle: 0.19634954084936207
C++ : 0.19509032201612825
C#  : 0.19509032201612828

2. Angle: 0.58904862254808621
C++ : 0.55557023301960218
C#  : 0.55557023301960229

3. Angle: 1.1780972450961724
C++ : 0.92387953251128674
C#  : 0.92387953251128663

4. Angle: 1.7671458676442586  
C++ : 0.98078528040323043
C#  : 0.98078528040323054
abenci
  • 8,422
  • 19
  • 69
  • 134
  • 2
    _exactly the same values_ this goes somewhat gainst the nature of floating point numbers; read they should be the same within a certain precision. Beyond that the concept of _exactly the same values_ becomes moot and those last digits should not be used anyway.. – TaW Jul 10 '20 at 10:59
  • 1
    Pinvoke the c++ implementation of the sin method – Hasan Emrah Süngü Jul 10 '20 at 11:00
  • what you link as "more details" does explain that there are different ways to calculate `sin`. I dont understand what more you expect as answer given that you didn't incldue any details of your actual problem – 463035818_is_not_an_ai Jul 10 '20 at 11:00
  • These small differences in sin() results become big differences in the results the whole library returns. – abenci Jul 10 '20 at 11:00
  • @hasan: we need a 100% .NET code conversion, sorry. – abenci Jul 10 '20 at 11:01
  • 1
    It is your responsibility to prevent that i.e. to understand that each number has only a limited precision. But of course you could write your own Sin approximation and call it from all applications. – TaW Jul 10 '20 at 11:02
  • Use `double` instead of `float`. – Dialecticus Jul 10 '20 at 11:02
  • @abenci PInvoke is something invokd from .Net right :P Aparat from semantics. as idclve mentioned in the comments the reason you get different results is exactly written in the link you provided. The sin method would even return different values when it is compiled in different environments, ie windows implementation might differ from a *nix iplementation – Hasan Emrah Süngü Jul 10 '20 at 11:05
  • 4
    That the final result is significantly dependent on those last digits show that the result is not really "meaningful" anyways because it is effectively dominated by inaccuracy. I would even go as far and say the results are faulty. Are your input data really that exact? In that case you might require working with a library that performs perfect precision instead of floats. For example BigInteger in C#. If both languages implement such a system, there should be no differences. However if your input data is not that accurate, the algorithm should not be allowed to depend on the accuracy of sin. – AlexGeorg Jul 10 '20 at 11:07
  • 1
    Or do you do some sort of physics simulation or other sort of iterative calculations where the result of the last calculation is input of the next? Yeah, that would be a tough case. Game developers who want to implement a replay feature in their physics based game have grown one or two gray hairs over the years due to this inaccuracy problem (since something like BgInteger is too slow). – AlexGeorg Jul 10 '20 at 11:11
  • Can you give some example inputs and outputs where they differ? – Kevin Jul 10 '20 at 12:46
  • I mean if it's really that important to you, you can (1) build a simple C++ program that calculates the sine of a number entered at runtime (2) debug and view the disassembly (3) step through until you get to the part that calculates the sine (static linking means the assembly is there) and (4) reverse engineer the actual code using simple control structures and arithmetic operations. That would PROBABLY give you complete guaranteed parity. You might be able to get hold of the math lib's sine implementation in some HLL, that could be even easier. – Patrick87 Jul 10 '20 at 13:29
  • C# `Math.Sin()` may use a different algorithm than used by C++ code. The link you provided has several different C or C++ implementations of sine, all of which differ. If you need sine to be consistent between C# and C++, you could make your own algorithm in C# and in C++ that produces the same output for the same given input. – Eljay Jul 10 '20 at 13:43
  • @TaW The IEEE specification recommends that `sin` should be *exact*. It's one of the functions we *do* expect (or at least, would like) to return the same exact floating point number on different implementations. Floating point math is only imprecise when you don't care to put in the work to make it precise. Having an exact `sin` is an important building block for other calculations. – HTNW Jul 10 '20 at 14:05
  • _IEEE specification recommends that sin should be exact._ Well, that may be so but it can't really mean anything other than _exact within the precision range of the given data type_. Understanding these _is an important building block for_ writing code. Of course one may take issue with ToString spitting out 1 digit more than the guranteed precision, which is why one may want to ignore that digit, as needed. – TaW Jul 10 '20 at 14:31
  • @kevin: result differs at the 16th and 17th decimals but completely change the order after a sorting operation on them: `C++ = 0.70710678118654757` `C# = 0.70710678118654746` – abenci Jul 10 '20 at 15:02
  • @TaW Well, there's only *one* exact `sin`. For every floating point number `x`, there is *exactly one* right answer to `sin(x)`. If we're getting two answers, *someone* has to be wrong. Now, as to the question: it looks like [`Math.Sin` (at least on Mono) uses the C `sin`](https://github.com/dotnet/runtime/blob/1905bde27967bff71c95ec5cbf2e099d80dce5f4/src/mono/mono/metadata/sysmath.c#L71-L75), which presumably `std::sin` also does. Is it possible that they *are* the same numbers, but are simply printed differently? Or perhaps the inputs are different. How are you generating the inputs? – HTNW Jul 10 '20 at 15:32
  • I think we can easily distinguish consistency (getting the same answer over and over), precision (getting an answer with lots of significant digits) and accuracy (getting an answer that is close to the true value of the sine function). The precision of doubles is fixed. In the best case, accuracy should be maximized by any function computing the sine function, and if two implementations both maximize accuracy (and round the same in case of a tie) then those implementations will be consistent (with themselves and each other). We just want consistency here. (c++ could be less accurate than C# !) – Patrick87 Jul 10 '20 at 15:37
  • [MSDN](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types) : _double ±5.0 × 10−324 to ±1.7 × 10308 ~15-17 digits 8 bytes_ Translation: __Do not use the 16th and 17th digit!__ - Further [reading](https://www.google.com/search?client=firefox-b-e&q=c%23+double+precision) – TaW Jul 10 '20 at 18:13
  • @abenci I cannot reproduce. Passing `std::nextafter(double(0.78539816339744830962), double(1))` (where the constant is `N[Pi/4, 20]` as taken from Mathematica) to `Math.Sin` and `std::sin` both give me (as seen by `std::cout << std::setprecision(17) << value`) `0.70710678118654757`, and passing just `0.78539816339744830962` causes both to give me `0.70710678118654746`. There's obviously no number in between that would give the mixed result. This makes me think the issue is a 1 ulp difference in the *inputs* to `Math.Sin`, not `Math.Sin` itself. – HTNW Jul 11 '20 at 01:49
  • @HTNW: this is not true for other angle values, see my edit in the original post. – abenci Jul 13 '20 at 06:37
  • 1
    I'm porting a C++ open source library to C# and lots of tests fail because std::sin and Math.Sin return different values and then it compares with an expected value using EXPECT_DOUBLE_EQ from GTest Suite, for example 0.78539816339744828 C++=0.70710678118654746 and C#=0.70710678118654757 [Test_S1ChordAngle_Arithmetic fails](https://github.com/alas/s2geometry/blob/main/S2GeometryTests/S1ChordAngleTests.cs) – Alas Feb 02 '22 at 04:01

0 Answers0