6

I have a problem with a simple multiplication that I can not understand... I am working with .Net Framework 4, and building in x86. I am executing the following code:

double x = 348333.673899683;
double y = 4521014.98461396;
double aux = x * y;

The expected value for aux is 1574821759346,09949827752137468 (I have done it with a simple calculator). However, the value I obtain in aux is 1574821822464. See that is not a precision error, even the integer part has been changed.

If I put a break point in the multiplication and hover the mouse over de * operator, I see x * y = 1574821759346.0994 which its ok. If I hover over the aux variable I see aux = 1574821822464

In order to clarify the last paragraph, two pictures can be seen below:

enter image description here

enter image description here

First, I have thought that maybe is because the x86 compilation, but reading the next post, I discard this option:

The Double Byte Size in 32 bit and 64 bit OS

I can not understand what is happening here. Any help will be appreciated.

---EDIT WITH MORE INFO---

I am using VS2015. I have added three more lines to debug it:

log.Info(x);
log.Info(y);
log.Info(aux);

To show the logs I am using the library log4net. The ouput is:

23322 [8] INFO Art.Model.Scenarios (null) - 348333,673899683
24745 [8] INFO Art.Model.Scenarios (null) - 4521014,98461396
26274 [8] INFO Art.Model.Scenarios (null) - 1574821822464

So it is not a bug in the debuger. If I create a completely new project and solution it works ok, but I can not understand why is not working in this solution.

---SECOND EDIT---

Thanks to the comments I have tried something new:

double x = 348333.673899683;
double y = 4521014.98461396;
double aux = x * y;

decimal xx = 348333.673899683m;
decimal yy = 4521014.98461396m;
decimal auxx = xx * yy;

log.Info(x);
log.Info(y);
log.Info(aux);

log.Info(xx);
log.Info(yy);
log.Info(auxx);

And the result is:

16129 [8] INFO Art.Model.Scenarios (null) - 348333,673899683
16145 [8] INFO Art.Model.Scenarios (null) - 4521014,98461396
16145 [8] INFO Art.Model.Scenarios (null) - 1574821822464
16145 [8] INFO Art.Model.Scenarios (null) - 348333,673899683
16145 [8] INFO Art.Model.Scenarios (null) - 4521014,98461396
16145 [8] INFO Art.Model.Scenarios (null) - 1574821759346,0994982775213747

So it works with the decimal but not with the double. Can someone explain this? I cannot understand why is happening.

Community
  • 1
  • 1
Ignacio
  • 806
  • 1
  • 10
  • 29
  • 4
    are you sure its not a debugger problem? what does Console.WriteLine say? I run this code in vs2015 c# interactive window and it gives the correct answer – pm100 May 25 '16 at 15:44
  • VS 2015 U2 execution window : 1574821759346.0994 – Dbl May 25 '16 at 15:52
  • I am using VS 2015 – Ignacio May 25 '16 at 15:53
  • 6
    Well, if it works fine in a new project and only fails in your project, then there is obviously something unusual about your current project. There is no possible way that we can tell you what without more information. A reproducible sample is *critical* to getting an answer to a question like this one. – Cody Gray - on strike May 25 '16 at 16:02
  • Is it really a _problem_ that you have _only_ 13 digits of precision instead of 15 or 16? If you need an _exact_ representation in decimal form then you should be using the `decimal` type instead. – D Stanley May 25 '16 at 16:04
  • @CodyGray I share your opinion, but maybe someone have had the same problem and can help me. – Ignacio May 25 '16 at 16:04
  • @DStanley Is not a precision problem. I can live with 2 decimal digits. The main problem is that even the integer part has changed – Ignacio May 25 '16 at 16:06
  • @Ignacio Ah - I didn't catch that. – D Stanley May 25 '16 at 16:11
  • It will work if you use decimals: https://dotnetfiddle.net/VSl1zC. You can't use floating point numbers for accuracy. See this question http://stackoverflow.com/questions/3814190/limiting-double-to-3-decimal-places and this article http://csharpindepth.com/Articles/General/FloatingPoint.aspx – Electric Sheep May 25 '16 at 16:18
  • what does `log.Info(aux.ToString("R", CultureInfo.InvariantCulture));` return? Please, notice, "R" format string – Dmitry Bychenko May 25 '16 at 16:18
  • @DmitryBychenko it show the same: 22263 [9] INFO Art.Model.Scenarios (null) - 1574821822464 – Ignacio May 25 '16 at 16:28
  • 1
    @ollie It [works just fine](https://dotnetfiddle.net/aDVc2z) with the double type, too. You are of course right that floating-point representations have a number of idiosyncrasies that surprise non-programmers, but the blanket statement that you cannot use them for accuracy, and the implication that you should always use the decimal type is rather silly. Not to mention misses the point of this question. – Cody Gray - on strike May 25 '16 at 16:30
  • 1
    @OllieP please, see edit number 2 – Ignacio May 25 '16 at 16:51
  • @LasseV.Karlsen but, in this case, should the result be the same but rounded? I am asking because even the integer part is changing. – Ignacio May 25 '16 at 17:02
  • And I too didn't catch that, it's not like you've made sure people *do* catch that part as it seems the question is only about lost precision behind the decimal point. Perhaps you should go edit the question to specifically call out the fact that the integer part changes as well? – Lasse V. Karlsen May 25 '16 at 17:12
  • @LasseV.Karlsen Thats true, I have done it. Thank you. – Ignacio May 25 '16 at 17:16
  • Then I'm unsure what this is. I am unable to reproduce this as my tooltip says ....346.0994, and the same is output from Console.WriteLine. I have tested Any CPU, x86, x64, release and debug of those 3, so 6 combinations. If I run without debugger I get the expected output, if I run with debugger I get the expected output and a breakpoint just after setting aux shows the right value in Visual Studio. – Lasse V. Karlsen May 25 '16 at 17:20
  • Is there anything about this scenario that you're not telling us? Such as this is really Mono, or you're really running this on a raspberry PI or something like that? Or is this stock .NET on a Windows machine using a current Intel or AMD processor? – Lasse V. Karlsen May 25 '16 at 17:24
  • Since you're saying that this only happens in this solution, *and* you have a variable named **aux**, do you do P/Invoke anywhere by any chance? Any chance the code you call into is messing with the cpu flags? – Lasse V. Karlsen May 25 '16 at 17:26
  • Windows 10 64bits, Intel processor. The only strange thing is that in the same project I have a reference to SlimDX, a framework for Direct X. Direct X uses the GPU, that have only 32 bits precision, but I am not even using it in the same class. Probably is not related. – Ignacio May 25 '16 at 17:30
  • The aux variable has no more references... – Ignacio May 25 '16 at 17:32
  • Could this somehow be an issue with culture? Your values use decimal points but your output uses commas. Could VS somehow be interpreting the double and decimal literals incorrectly? Or could the `log.Info` method have something to do with it? Could you show the code for that method? – Chris Dunaway May 25 '16 at 19:42
  • It is a external library called log4net so I am pretty sure that it works ok... I have seen that at some point of the code before the execution of this piece, a LoaderLock exception is thrown (I have the LoaderLock exceptions disabled, that is why I havent mentioned it before. Coud it be related? – Ignacio May 25 '16 at 20:15
  • 1
    If I cast both `x` and `y` to float before doing the multiplication, I get 1574821822464. I think the problem may be to do with 32 bit vs. 64 bit floating point arithmetic. – Patricia Shanahan May 26 '16 at 14:48
  • 1
    What does System.Runtime.InteropServices.Marshal.SizeOf(aux) return? – TJF Jul 21 '16 at 03:07

2 Answers2

3

Most likely, in case you are using DirectX (the only reason I can find behind your problematic), this problem seems related to the fact that every time a Device is created and/or handled, it forces the FPU to single precision mode, thus losing accuracy and causing double, long, decimal variables to get truncated. If I try a IEEE-754 floating point converter and input your data, I get this result, which is precisely your case: your data, at some point, was read as a double-precision number, but then it got truncated into a single.precision floating point number as you can see:

enter image description here

This problem can be solved by explicitly building the Device object under the flag FpuPreserve.

I also had this very problem, and in the beginning though about incorrect casting or so, until after a long trace found out the values got truncated after I built a DirectX Device object.

JJJ
  • 32,902
  • 20
  • 89
  • 102
1

The phenomenon occurs, because of the definition of the datatypes.

double: “Precision. When you work with floating-point numbers, remember that they do not always have a precise representation in memory.” https://msdn.microsoft.com/en-us//de/library/x99xtshc.aspx

decimal: “Compared to floating-point types, the decimal type has more precision and a smaller range, which makes it appropriate for financial and monetary calculations.” https://msdn.microsoft.com/en-us//library/364x0z75.aspx

Double Data Types are stored as binary fraction, because of that, they cannot exact represented, if that is not a binary fraction. https://msdn.microsoft.com/en-us//ae382yt8

naro
  • 416
  • 7
  • 16