https://bugs.winehq.org/show_bug.cgi?id=38988
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |dotnet Status|UNCONFIRMED |NEW CC| |focht@gmx.net Component|-unknown |oleaut32 Summary|when using MS .net there |.NET applications using |are wrong results (but no |System.Decimal to float |errors) |conversion may return wrong | |results ('VarR4FromDec' | |divisor integer overflow) Ever confirmed|0 |1
--- Comment #4 from Anastasius Focht focht@gmx.net --- Hello folks,
confirming.
Prerequisite: 'winetricks -q dotnet40'
IL code of the app using 'ILSpy':
--- snip --- .method private hidebysig static void Main ( string[] args ) cil managed { // Method begins at RVA 0x2050 // Code size 161 (0xa1) .maxstack 6 .entrypoint .locals init ( [0] valuetype [mscorlib]System.Decimal, [1] float32 )
IL_0000: ldc.i4 1353919093 IL_0005: ldc.i4 138 IL_000a: ldc.i4.0 IL_000b: ldc.i4 128 IL_0010: ldc.i4.s 12 IL_0012: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, uint8) IL_0017: stloc.0 IL_0018: ldloc.0 IL_0019: call float32 [mscorlib]System.Decimal::op_Explicit(valuetype [mscorlib]System.Decimal) IL_001e: conv.r4 IL_001f: stloc.1 IL_0020: ldstr "Value as decimal: " IL_0025: ldloc.0 IL_0026: box [mscorlib]System.Decimal IL_002b: call string [mscorlib]System.String::Concat(object, object) IL_0030: call void [mscorlib]System.Console::WriteLine(string) IL_0035: ldstr "Value converted to float: " IL_003a: ldloc.1 IL_003b: box [mscorlib]System.Single IL_0040: call string [mscorlib]System.String::Concat(object, object) IL_0045: call void [mscorlib]System.Console::WriteLine(string) IL_004a: ldc.i4 -268880975 IL_004f: ldc.i4 140 IL_0054: ldc.i4.0 IL_0055: ldc.i4.0 IL_0056: ldc.i4.s 12 IL_0058: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, uint8) IL_005d: stloc.0 IL_005e: ldloc.0 IL_005f: call float32 [mscorlib]System.Decimal::op_Explicit(valuetype [mscorlib]System.Decimal) IL_0064: conv.r4 IL_0065: stloc.1 IL_0066: ldstr "Value as decimal: " IL_006b: ldloc.0 IL_006c: box [mscorlib]System.Decimal IL_0071: call string [mscorlib]System.String::Concat(object, object) IL_0076: call void [mscorlib]System.Console::WriteLine(string) IL_007b: ldstr "Value converted to float: " IL_0080: ldloc.1 IL_0081: box [mscorlib]System.Single IL_0086: call string [mscorlib]System.String::Concat(object, object) IL_008b: call void [mscorlib]System.Console::WriteLine(string) IL_0090: ldstr "\n<RETURN>" IL_0095: call void [mscorlib]System.Console::WriteLine(string) IL_009a: call string [mscorlib]System.Console::ReadLine() IL_009f: pop IL_00a0: ret } // end of method Program::Main --- snip ---
'Decimal' constructor '(Int32, Int32, Int32, Boolean, Byte)':
https://msdn.microsoft.com/en-us/library/bb1c1a6x.aspx
--- quote --- public Decimal( int lo, int mid, int hi, bool isNegative, byte scale ) ... Parameters
lo Type: System.Int32
The low 32 bits of a 96-bit integer.
mid Type: System.Int32
The middle 32 bits of a 96-bit integer.
hi Type: System.Int32
The high 32 bits of a 96-bit integer.
isNegative Type: System.Boolean
true to indicate a negative number; false to indicate a positive number.
scale Type: System.Byte
A power of 10 ranging from 0 to 28.
...
Remarks
The binary representation of a Decimal number consists of a 1-bit sign, a 96-bit integer number, and a scaling factor used to divide the integer number and specify what portion of it is a decimal fraction. The scaling factor is implicitly the number 10 raised to an exponent ranging from 0 to 28. --- quote ---
The first number -0.594059405941:
--- snip --- IL_0000: ldc.i4 1353919093 IL_0005: ldc.i4 138 IL_000a: ldc.i4.0 IL_000b: ldc.i4 128 IL_0010: ldc.i4.s 12 --- snip ---
Using 'bc' command line tool to check the compiler did the right thing on emitting DECIMAL parts for '-0.594059405941m':
--- snip --- $ echo "scale=20; -(138*2^32+1353919093)/10^12" | bc -.59405940594100000000 --- snip ---
Yep.
--- snip --- $ Wine-dbg>info locals
0x7e679c3f VarR4FromDec+0x3d: (0033f228) DECIMAL* pDecIn=0x33f23c (parameter [EBP+8]) float* pFltOut=0x33f238 (parameter [EBP+12]) BYTE scale=12 (local [ESP+39]) int divisor=0x1 (local [ESP+32]) double highPart=...(local [ESP+24])
Wine-dbg>p *pDecIn {wReserved=0, u={s={scale=12, sign=-128}, signscale=0x800c}, Hi32=0, u1={s1={Lo32=0x50b32a75, Mid32=0x8a}, Lo64=0x8a50b32a75}} --- snip ---
Source: https://source.winehq.org/git/wine.git/blob/bedd444a3616fea54a060e07c27a137f...
--- snip --- 2948 HRESULT WINAPI VarR4FromDec(DECIMAL* pDecIn, float *pFltOut) 2949 { 2950 BYTE scale = DEC_SCALE(pDecIn); 2951 int divisor = 1; 2952 double highPart; 2953 2954 if (scale > DEC_MAX_SCALE || DEC_SIGN(pDecIn) & ~DECIMAL_NEG) 2955 return E_INVALIDARG; 2956 2957 while (scale--) 2958 divisor *= 10; 2959 2960 if (DEC_SIGN(pDecIn)) 2961 divisor = -divisor; 2962 2963 if (DEC_HI32(pDecIn)) 2964 { 2965 highPart = (double)DEC_HI32(pDecIn) / (double)divisor; 2966 highPart *= 4294967296.0F; 2967 highPart *= 4294967296.0F; 2968 } 2969 else 2970 highPart = 0.0; 2971 2972 *pFltOut = (double)DEC_LO64(pDecIn) / (double)divisor + highPart; 2973 return S_OK; 2974 } --- snip ---
Integer overflow for divisor. The data type must cover a range of at least -1e+28..1e+28 here.
$ wine --version wine-1.7.48
Regards