[Mono-list] Mono and .Net Floating Point Inconsistencies
Kornél Pál
kornelpal at gmail.com
Fri Jan 16 12:58:22 EST 2009
Hi,
If you are sure that this is the case you should file a bug report
because this could easily be solved in the runtime.
Kornél
Dallman, John wrote:
>> when I take the same binary and run it with the same inputs it
>> produces different outputs if it is run on mono and .Net.
>
> This is with Mono on Linux, and .NET on Windows? The executable
> is 32-bit .NET code?
>
> I suspect that you've hit a misfeature that exists for most
> floating-point code on 32-bit x86 Linux. It goes like this.
>
> The 32-bit floating-point registers (the "x87 registers") are
> quite large, 80 bits long. This allows more precision than
> conventional "double" variables, which are 64 bits. There's a
> control register for setting what precision the processor should
> evaluate to, which has options of 32 bits ("float"), 64 bits
> ("double") or 80 bits ("long double"). The idea was that you set
> up the precision you wanted to use, and that lower precision was
> faster. The power-up default is 80 bits, and Linux doesn't change
> that.
>
> However, lower precision is not faster any more. With tens of
> millions of transistors available, rather than tens of thousands,
> chip designers can use different floating-point methods that are
> much faster. So using 80bit precision is obviously the right
> thing to do? Well, no.
>
> You see, doubles in memory are 64 bits. And if you use 80 bit
> precision in the floating point processor, that means that when
> intermediate values get saved out to memory, they get rounded off
> to 64 bits, and then extended back to 80 bits when they're re-
> loaded. And those extension bits won't be the same as the ones
> that were discarded. Sadly, this is the way that floating point
> behaves by default on 32-bit x86 Linux. It introduces some noise
> into the results; one can't so much say that they are wrong, as
> that they aren't quite consistent with other platforms that use
> 64 bits consistently throughout the calculation process.
>
> Sparcs, PowerPCs, ARMs, and 64-bit x86 use 64-bit floating-point
> consistently. 32-bit Windows used to do floating-point the same
> way as 32-bit Linux does, but they changed it, so that when a
> Windows program starts up, the floating point has been set up to
> do 64 bits throughout. That's much more consistent with other
> platforms, and is an interesting instance of MS doing something
> arguably "right even though awkward" instead of "consistent with
> past errors".
>
> But why not change it to whatever you need? Well, changing the
> floating point precision is Very Slow on modern processors. You
> have to completely flush the pipeline at minimum. It's much easier
> for it to be set at program start-up, and used consistently after
> that.
>
> To get this to work right in Mono, you need to write a small C
> function, and call it with P/Invoke on Linux. That means your
> program won't be pure .NET code anymore; how to best cope with
> that depends on your program.
>
> The code to be run in the C function is:
>
> #include <fpu_control.h> /* Mask the Denormal, Underflow and Inexact
> exceptions,
> leaving Invalid, Overflow and
> Zero-divide active.
> Set precision to standard doubles,
> and round-to-nearest. */
> fpu_control_t desired = _FPU_MASK_DM | _FPU_MASK_UM | _FPU_MASK_PM |
> _FPU_DOUBLE | _FPU_RC_NEAREST ;
> _FPU_SETCW( desired);
>
> This needs to be a C function because everything in uppercase in
> that code is a macro, from fpu_control.h. You may want to leave out
> enabling floating point traps, in which case the code becomes:
>
> #include <fpu_control.h> /* Set precision to standard doubles, and
> round-to-nearest. */
> fpu_control_t desired = _FPU_DOUBLE | _FPU_RC_NEAREST ;
> _FPU_SETCW( desired);
>
> It would be good, really, if Mono had a standard call for setting
> up consistent floating-point on all its platforms.
>
More information about the Mono-list
mailing list