Jump to content

  • Log In with Google      Sign In   
  • Create Account


Identical math code working differently on C# and C++


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
17 replies to this topic

#1 vadevaman   Members   -  Reputation: 157

Like
1Likes
Like

Posted 30 May 2014 - 06:50 AM

So basically I prototyped and developed a math code (for vechicle simulations) in C# and it works perfectly. Now when I port the code to C++

so that I could acces it in a C# environment through a DLL I get some junky behaviour.

 

For example when I run my physics program, the cars driving wheels start accelerating with no reason and it seems like it's mostly caused by rolling resistance, wich by the way works great in C# environment.

This rolling resistance relys on a value that is received from C# environment (from C# float to c++ float)

Another problem is caused by slip ratio and slip angle, the wheels start shaking at low speeds, wich again does not happen in the C# version.

Slip calcs both rely also on values received from C# environment.

 

So might the float conversion from C# to C++ be the case?

 

This stands in my exportFunctions.cpp:

EXPORT_API void Dll_Wheel_SetVelocity(CarWheel* wheel,float lonRoadVelocity,float latRoadVelocity){    wheel->SetVelocity(lonRoadVelocity,latRoadVelocity);}

 

These 2 velocityes are used for the calculations that cause those bug's.  

 

And no I cant share the code, but I can give closer information on how it looks like.

 

Thanks!

 

Edit:

Strange part is that this acceleration issue only occurs in Debug build. If I build the dll as a release, then this issue is vanished, but small friction error still remains. Actually it's only causing the smoke to come out of the tires and tireq squal sound to emmit, but physically it seems to be ok.

But I still suspect that there is some sort of a error somewhere, because debug & release should work identically shouldnt they?


Edited by vadevaman, 30 May 2014 - 07:25 AM.


Sponsor:

#2 Andy Gainey   Members   -  Reputation: 1992

Like
5Likes
Like

Posted 30 May 2014 - 09:34 AM

One speculation that comes to mind is that you might have some uninitialized variables causing problems in your C++ port.  In release mode, you might be getting lucky and the memory usually happens to be already set to 0.  But in debug mode, the compiler might specifically be going out of its way to set the memory to "bad" values, so that any problems are painfully evident.

C# on the other hand has more strict rules about initializing variable values, so you can pretty much be guaranteed no junk data.



"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke

#3 phil_t   Crossbones+   -  Reputation: 3261

Like
4Likes
Like

Posted 30 May 2014 - 09:43 AM


But I still suspect that there is some sort of a error somewhere, because debug & release should work identically shouldnt they?

 

I know .net/C# can produce different values for floating calculations depending on optimizations made by the JIT (floating point calculations are implicitly converted to double in one case but not the other) . I haven't seen any differences between Debug and Release, but I have seen differences between debugger attached and no debugger attached (see here).



#4 vadevaman   Members   -  Reputation: 157

Like
0Likes
Like

Posted 30 May 2014 - 02:02 PM


I have seen differences between debugger attached and no debugger attached (see here).

 

So after reading this article, as I understand should use double instead in C++ ?

I think float's size is 4 for both C# and C++ and I'm not sure why'd there be a problem when passing a float from C# to C++
but after looking this I got confirmed, that there can be a difference...

 

Anyway thanks for explaining.



#5 vadevaman   Members   -  Reputation: 157

Like
0Likes
Like

Posted 31 May 2014 - 01:35 PM

Still fighting with this.
After setting the flotaing point model to /fp:fast in my compiler (msvc2010) it should have now get the exact same float's from the dll but the "flickering" issue still remains, and I suspect its from very small values like 3.21-e8 etc. because when the car is stand still, it still causes minimalistic movements.
Now when I pass them to my DLL something gets messed... So maybe its the way I pass them?

Anyway this is how I pass these speccific values in C# side:
 

namespace Example
[
	public class Wheel
	{		
		public bool loging = true;
		#region Members		
		public IntPtr nativeWheelObject; 
	    #endregion Members

		public Wheel()
		{			
        	this.nativeWheelObject = Sim.Dll_Wheel_Add();
			return;
		}
		
		#region Wrapper methods	
		public void SetVelocity(float lonRoadVelocity,float latRoadVelocity){Sim.Dll_Wheel_SetVelocity(this.nativeWheelObject,lonRoadVelocity,latRoadVelocity);}
		#endregion Wrapper methods
	}
	
	internal class Sim
	{
    	#region PInvokes
		[DllImport(pluginName, CallingConvention=CallingConvention.Cdecl)] public static extern void Dll_Wheel_SetVelocity(IntPtr wheel,float lonRoadVelocity,float latRoadVelocity);
		#endregion PInvokes	
	}		
}

This much code I can show.



#6 phil_t   Crossbones+   -  Reputation: 3261

Like
0Likes
Like

Posted 31 May 2014 - 01:43 PM

What have you done to debug the issue? Can't you set breakpoints on either side of the .net/native transition to verify the values are the same?

 

Also, what makes you think it's a problem with the interop, rather than with the port of your actual math code?



#7 vadevaman   Members   -  Reputation: 157

Like
0Likes
Like

Posted 31 May 2014 - 02:26 PM

I atached the debugger to the program and set the breaking point to where the problem occurs.

c++ lonVel    -0.036019072    float
c# lonVel    -0.029392920    float

As you can see there is a noticable difference. Even after /fp:fast

I also tryed to use the values a double and convert it to float and that didnt work at all. Probably did it wrong anyways.

 

The reason why I think my math port isnt the case, is that the math is rather simple and straight forward and mostly copy/paste from the C# code. And the actual proof that the transfered values are different.
But I guess the math gets a bit messed aswell because I also transfer the TimeStep from C# and use it in the math calcs.



#8 Adam_42   Crossbones+   -  Reputation: 2462

Like
0Likes
Like

Posted 31 May 2014 - 06:01 PM

Specifying /arch:SSE2 might get you a bit closer.

 

However, you shouldn't even expect your C# code to produce identical results on all systems - it will almost certainly differ between x86 and x64 systems for example.



#9 Bacterius   Crossbones+   -  Reputation: 8584

Like
5Likes
Like

Posted 31 May 2014 - 06:37 PM

Why all the downvotes on the OP? Sure, it's a bit open to speculation due to the lack of code, but not all code can be shared and floating-point determinism is a valid and very relevant question.


The slowsort algorithm is a perfect illustration of the multiply and surrender paradigm, which is perhaps the single most important paradigm in the development of reluctant algorithms. The basic multiply and surrender strategy consists in replacing the problem at hand by two or more subproblems, each slightly simpler than the original, and continue multiplying subproblems and subsubproblems recursively in this fashion as long as possible. At some point the subproblems will all become so simple that their solution can no longer be postponed, and we will have to surrender. Experience shows that, in most cases, by the time this point is reached the total work will be substantially higher than what could have been wasted by a more direct approach.

 

- Pessimal Algorithms and Simplexity Analysis


#10 vadevaman   Members   -  Reputation: 157

Like
0Likes
Like

Posted 01 June 2014 - 04:42 AM


/arch:SSE2

This didnt help also. Saddly I need these 2 values to be correct, otherwise my math calcs will be broken... This numerical chage doesnt seem huge, but it's really huge for my calcs.

 

Is there any tutorial or a post atleast, on how to pass a small float wit hatleast 90-99% of accuracy from C# environment to c++ environment. And t obe more specific I'm using c# mono.
I dont want to believe that there isnt a way, when there clearly must be a way. Maybe a new custom data type? one that is compatible with C# float's?

 

I'm really worried because this has been a huge setback in my coding for a while. I cant release my products, because of this. My product works ok in C# but I need to port the code to C++ for security reasons, as you know C# can be easily decompiled.



#11 kunos   Crossbones+   -  Reputation: 2205

Like
0Likes
Like

Posted 01 June 2014 - 05:01 AM

perhaps you could try to pass it over the boundary as double and cast it back to float on the C side.

 

Just to make sure, you are saying that you have a function:

 

void myCFunction(float value);

 

On the C side.. and if you call it from C# with a value myCFunction(0.2899344f); you receive something different on the C side?


Edited by kunos, 01 June 2014 - 05:02 AM.

Stefano Casillo
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#12 Adam_42   Crossbones+   -  Reputation: 2462

Like
1Likes
Like

Posted 01 June 2014 - 05:09 AM

The main problem isn't passing values to and from C#.

 

The way the two different languages transform calculations into assembly is different. Most notably in both cases some of the intermediate calculations may be done at a higher precision than you've specified. There will also be small differences in the results from library functions like sin(). In addition C# depending on which CPU it's run on may generate different assembly when it's JITted, so the C# result almost certainly won't be identical between different PCs.

 

If you want reproducibility integer maths is much easier to do that with.

 

C++ can also be easily disassembled. It's impossible to keep any calculation you do on a computer secret from the owner of that computer. If this is for something like license key validation you should switch to something based on public key cryptography.



#13 kunos   Crossbones+   -  Reputation: 2205

Like
0Likes
Like

Posted 01 June 2014 - 05:14 AM

C++ can also be easily disassembled.

 

 

 

yes but no... in C# you can get back to the names of the functions and variables. Surely in C++ you will still read the names of the function but the code inside will be much harder to reverse engineering. I understand where the OP is coming from because I went through exactly the same decisions.

 

I wonder if the OP tested the new .NET native.. that might solve the problem without need to leave C# land.


Stefano Casillo
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#14 vadevaman   Members   -  Reputation: 157

Like
0Likes
Like

Posted 01 June 2014 - 05:37 AM


perhaps you could try to pass it over the boundary as double and cast it back to float on the C side.

 

Well I tryed it like this:
C#

public void SetVelocity(float lonRoadVelocity,float latRoadVelocity){Sim.Dll_Wheel_SetVelocity(this.nativeWheelObject,lonRoadVelocity,latRoadVelocity);}

  C++

    EXPORT_API void Dll_Wheel_SetVelocity(CarWheel* wheel,double lonRoadVelocity,double latRoadVelocity){    wheel->SetVelocity((float)lonRoadVelocity,(float)latRoadVelocity);}

And the result was, that nothing was transfered... the values stayed 0.

 

And about the question, to be specific I'm not using C, I'm using C++ (the languages should be different)

And currently I'm passing them the way as you described.

 

 

Also after some deeper debuging I found out that strangely the difference happens in my wheel class...

void CarWheel::SetVelocity(float lonVel,float latVel)
{
	lonRoadVelocity = lonVel;
	latRoadVelocity = latVel;
}

"lonRoadVelocity = lonVel" is actually "lonRoadVelocity != lonVel"

So its internal??? C++ float mismatched with C++ float? I'm confused....

 

Edit:

The above "internal" was a false alarm I guess.

Because I removed the "SetVelocity" method and pass the values directly, wheel->lonRoadVelocity = lonRoadVelocity;

and there still is difference, I think I was just confused,

 

Debug Results:

		latRoadVelocity	-0.15862428	float
		lonRoadVelocity	-0.036250707	float
+		wheel	0x1b75e120 {brake={...} tire={...} suspension={...} ...}	CarWheel *
		wheel->latRoadVelocity	-0.15102120	float
		wheel->lonRoadVelocity	-0.036250707	float

After /arch:SSE2 and /fp:fast the difference actually is notably smaller. In this case for some reason it only occurs in "latRoadVelocity".

"lonRoadVelocity" seems to be 1:1 o nevery frame I debuged, so I'm getting closer


Edited by vadevaman, 01 June 2014 - 06:30 AM.


#15 kunos   Crossbones+   -  Reputation: 2205

Like
0Likes
Like

Posted 01 June 2014 - 06:04 AM

hm weird.. in C# if the signature of the function requires a double, it shouldn't compile if you pass a float to it... did you change the signature of the DLL_Set_velocity on the C# side?


Stefano Casillo
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#16 vadevaman   Members   -  Reputation: 157

Like
0Likes
Like

Posted 01 June 2014 - 06:44 AM

I just noticed there is a inacuracy issue with floats in C++, flaots in C# (atleast in mono) stay the same and are pretty good to work with

but in C++ a float of 0.0091 is actually 0.0091000004 ... that 4 in the end can make a math calc result in wrong calculations.

 

While "lonRoadVelocity" seems to pass normally now, I dont understnd why the "latRoadVelocity" has a offset, when I pass it the same way...
 

 

 

 


in C# if the signature of the function requires a double

Sounds right indeed. Maybe it's because I'm using mono?
Anyway when passing a float instead of double, the values stay 0, but when I cast the float to double in C# and pass the double ... well saddly nothing changes.

 

But like I mentioned, the "lonRoadVelocity" seems to now be 1:1, but for some strange reason the "latRoadVelocity" has an offset.

 

Edit:

Here are some debug results in a single frame:

In aplication (C#)

debugcsharp.jpg

and results in msvc2010 debug:

w0
lonRoadVelocity    0.027051473    float
latRoadVelocity    0.13362961    float
wheel->lonRoadVelocity    0.027051473    float
wheel->latRoadVelocity    0.14100081    float
w1
lonRoadVelocity    -0.034490682    float
latRoadVelocity    0.13361663    float
wheel->lonRoadVelocity    -0.034490682    float
wheel->latRoadVelocity    0.14098407    float
w2
lonRoadVelocity    0.026764294    float
latRoadVelocity    0.030822765    float
wheel->lonRoadVelocity    0.026764294    float
wheel->latRoadVelocity    0.043717053    float        
w3
lonRoadVelocity    -0.034203805    float
latRoadVelocity    0.030835744    float
wheel->lonRoadVelocity    -0.034203805    float
wheel->latRoadVelocity    0.043733794    float

Edited by vadevaman, 01 June 2014 - 07:16 AM.


#17 unbird   Crossbones+   -  Reputation: 4973

Like
1Likes
Like

Posted 01 June 2014 - 07:38 AM

So the parameters are fine. Now where might that breakpoint be ?

 

Also: 0.0091000004 is within float precision of 0.0091. It's probably also just a difference with the debug view, not the actual value.



#18 vadevaman   Members   -  Reputation: 157

Like
0Likes
Like

Posted 01 June 2014 - 07:47 AM


So the parameters are fine.

At w2 lateral speeds:

actual value 0.043717053

"equal" value 0.030822765
0.043717053 - 0.030822765 = 0.012894288
this is 34.59% of difference how come this be fine?

Breakpoint is at my exportFunctions.cpp

 

 

Edit: I just noticed something, the latRoadVelocity seem to be used form the last frame... I'll have a deeper look on my main class

 

Edit2: This is odd, seems like the values are passed quite accurate now, but the calculations are still messed and the wheel's cant find theyr correct slip's...
Is there a possibility that the math function (wich is quite simple) may produce absolutely different results, causing the math to break?


Edited by vadevaman, 01 June 2014 - 09:51 AM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS