Bladeren bron

resolves #15453:
* fixes i386 DivMod with negative numbers with a patch from Jonas
* fixes generic DivMod with negative numbers
* test updated

git-svn-id: trunk@14532 -

florian 15 jaren geleden
bovenliggende
commit
31e2f16484
3 gewijzigde bestanden met toevoegingen van 82 en 31 verwijderingen
  1. 19 20
      rtl/i386/mathu.inc
  2. 49 11
      rtl/objpas/math.pp
  3. 14 0
      tests/test/units/math/tdivmod.pp

+ 19 - 20
rtl/i386/mathu.inc

@@ -61,29 +61,27 @@ function cotan(x : float) : float;assembler;
 {$define FPC_MATH_HAS_DIVMOD}
 procedure DivMod(Dividend: Integer; Divisor: Word; var Result, Remainder: Word);assembler;
 asm
-  pushw %di
-  movw %dx,%di
-  movl %eax,%edx
-  shrl $16,%edx
-  div %di
-  movw %ax,(%ecx)
-  movl Remainder,%ecx
-  movw %dx,(%ecx)
-  popw %di
+  pushl  %edi
+  movzwl %dx,%edi
+  cltd
+  idiv   %edi
+  movw   %ax,(%ecx)
+  movl   Remainder,%ecx
+  movw   %dx,(%ecx)
+  popl   %edi
 end;
 
 
 procedure DivMod(Dividend: Integer; Divisor: Word; var Result, Remainder: SmallInt);assembler;
 asm
-  pushw %di
-  movw %dx,%di
-  movl %eax,%edx
-  shrl $16,%edx
-  div %di
-  movw %ax,(%ecx)
-  movl Remainder,%ecx
-  movw %dx,(%ecx)
-  popw %di
+  pushl  %edi
+  movzwl %dx,%edi
+  cltd
+  idiv   %edi
+  movw   %ax,(%ecx)
+  movl   Remainder,%ecx
+  movw   %dx,(%ecx)
+  popl   %edi
 end;
 
 
@@ -92,7 +90,7 @@ asm
   pushl %edi
   movl %edx,%edi
   xorl %edx,%edx
-  div %edi
+  div  %edi
   movl %eax,(%ecx)
   movl Remainder,%ecx
   movl %edx,(%ecx)
@@ -104,7 +102,7 @@ procedure DivMod(Dividend: Integer; Divisor: Integer; var Result, Remainder: Int
 asm
   pushl %edi
   movl %edx,%edi
-  xorl %edx,%edx
+  cltd
   idiv %edi
   movl %eax,(%ecx)
   movl Remainder,%ecx
@@ -112,6 +110,7 @@ asm
   popl %edi
 end;
 
+
 function GetRoundMode: TFPURoundingMode;
 begin
   Result := TFPURoundingMode((Get8087CW shr 10) and 3);

+ 49 - 11
rtl/objpas/math.pp

@@ -2221,33 +2221,71 @@ end;
 {$ifndef FPC_MATH_HAS_DIVMOD}
 procedure DivMod(Dividend: Integer; Divisor: Word; var Result, Remainder: Word);
 begin
-  Result:=Dividend Div Divisor;
-  Remainder:=Dividend -(Result*Divisor);
+  if Dividend < 0 then
+    begin
+      { Use DivMod with >=0 dividend }
+	  Dividend:=-Dividend;
+      { The documented behavior of Pascal's div/mod operators and DivMod
+        on negative dividends is to return Result closer to zero and
+        a negative Remainder. Which means that we can just negate both
+        Result and Remainder, and all it's Ok. }
+      Result:=-(Dividend Div Divisor);
+      Remainder:=-(Dividend+(Result*Divisor));
+    end 
+  else
+    begin
+	  Result:=Dividend Div Divisor;
+      Remainder:=Dividend-(Result*Divisor);
+	end;
 end;
 
 
 procedure DivMod(Dividend: Integer; Divisor: Word; var Result, Remainder: SmallInt);
-var
-  UnsignedResult: Word absolute Result;
-  UnsignedRemainder: Word absolute Remainder;
 begin
-  DivMod(Dividend, Divisor, UnsignedResult, UnsignedRemainder);
+  if Dividend < 0 then
+    begin
+      { Use DivMod with >=0 dividend }
+	  Dividend:=-Dividend;
+      { The documented behavior of Pascal's div/mod operators and DivMod
+        on negative dividends is to return Result closer to zero and
+        a negative Remainder. Which means that we can just negate both
+        Result and Remainder, and all it's Ok. }
+      Result:=-(Dividend Div Divisor);
+      Remainder:=-(Dividend+(Result*Divisor));
+    end 
+  else
+    begin
+	  Result:=Dividend Div Divisor;
+      Remainder:=Dividend-(Result*Divisor);
+	end;
 end;
 
 
 procedure DivMod(Dividend: DWord; Divisor: DWord; var Result, Remainder: DWord);
 begin
   Result:=Dividend Div Divisor;
-  Remainder:=Dividend -(Result*Divisor);
+  Remainder:=Dividend-(Result*Divisor);
 end;
 
 
 procedure DivMod(Dividend: Integer; Divisor: Integer; var Result, Remainder: Integer);
-var
-  UnsignedResult: DWord absolute Result;
-  UnsignedRemainder: DWord absolute Remainder;
 begin
-  DivMod(Dividend, Divisor, UnsignedResult, UnsignedRemainder);
+  if Dividend < 0 then
+    begin
+      { Use DivMod with >=0 dividend }
+	  Dividend:=-Dividend;
+      { The documented behavior of Pascal's div/mod operators and DivMod
+        on negative dividends is to return Result closer to zero and
+        a negative Remainder. Which means that we can just negate both
+        Result and Remainder, and all it's Ok. }
+      Result:=-(Dividend Div Divisor);
+      Remainder:=-(Dividend+(Result*Divisor));
+    end 
+  else
+    begin
+	  Result:=Dividend Div Divisor;
+      Remainder:=Dividend-(Result*Divisor);
+	end;
 end;
 {$endif FPC_MATH_HAS_DIVMOD}
 

+ 14 - 0
tests/test/units/math/tdivmod.pp

@@ -1,3 +1,4 @@
+{$mode objfpc}
 uses
   math;
 { tests:
@@ -55,5 +56,18 @@ begin
     doerror(3003);
   if RemainderInteger<>15 then
     doerror(3004);
+	
+  DivMod(-9, 5, QuotientInteger,RemainderInteger);
+  if QuotientInteger<>-1 then
+    doerror(3005);
+  if RemainderInteger<>-4 then
+    doerror(3006);
+	
+  DivMod(-9, -5, QuotientInteger,RemainderInteger);
+  if QuotientInteger<>1 then
+    doerror(3007);
+  if RemainderInteger<>-4 then
+    doerror(3008);
+	
 end.