Просмотр исходного кода

* Replaced fpc_round_real with new implementation, having the following important properties:
- it does not directly depend on softfloat stuff.
- it triggers 'inexact' condition in a way compatible to hardware instructions (second assertion in webtbs/tw3157.pp now works correctly).

git-svn-id: trunk@27197 -

sergei 11 лет назад
Родитель
Сommit
fffc317759
1 измененных файлов с 41 добавлено и 44 удалено
  1. 41 44
      rtl/inc/genmath.inc

+ 41 - 44
rtl/inc/genmath.inc

@@ -1281,53 +1281,50 @@ type
 {$ifndef FPC_SYSTEM_HAS_ROUND}
     function fpc_round_real(d : ValReal) : int64;compilerproc;
      var
-      fr: ValReal;
-      tr: Int64;
+      tmp: double;
+      j0: longint;
+      hx: longword;
+      sx: longint;
+    const
+      H2_52: array[0..1] of double = (
+        4.50359962737049600000e+15,
+       -4.50359962737049600000e+15
+      );
     Begin
-      fr := abs(Frac(d));
-      tr := Trunc(d);
-      result:=0;
-      case softfloat_rounding_mode of
-        float_round_nearest_even:
-          begin
-            if fr > 0.5 then
-              if d >= 0 then
-                result:=tr+1
-              else
-                result:=tr-1
-            else
-            if fr < 0.5 then
-               result:=tr
-            else { fr = 0.5 }
-               { check sign to decide ... }
-               { as in Turbo Pascal...    }
-              begin
-                if d >= 0.0 then
-                  result:=tr+1
-                else
-                  result:=tr;
-                { round to even }
-                result:=result and not(1);
-              end;
-          end;
-        float_round_down:
-          if (d >= 0.0) or
-             (fr = 0.0) then
-            result:=tr
+      { This basically calculates trunc((d+2**52)-2**52) }
+      hx:=float64high(d);
+      j0:=((hx shr 20) and $7ff) - $3ff;
+      sx:=hx shr 31;
+      hx:=(hx and $fffff) or $100000;
+
+      if j0>=52 then         { No fraction bits, already integer }
+        begin
+          if j0>=63 then     { Overflow, let trunc() raise an exception }
+            exit(trunc(d))   { and/or return +/-MaxInt64 if it's masked }
           else
-            result:=tr-1;
-        float_round_up:
-          if (d >= 0.0) and
-             (fr <> 0.0) then
-            result:=tr+1
+            result:=((int64(hx) shl 32) or float64low(d)) shl (j0-52);
+        end
+      else
+        begin
+          { Rounding happens here. It is important that the expression is not
+            optimized by selecting a larger type to store 'tmp'. }
+          tmp:=H2_52[sx]+d;
+          d:=tmp-H2_52[sx];
+          hx:=float64high(d);
+          j0:=((hx shr 20) and $7ff)-$3ff;
+          hx:=(hx and $fffff) or $100000;
+          if j0<=20 then
+            begin
+              if j0<0 then
+                exit(0)
+              else           { more than 32 fraction bits, low dword discarded }
+                result:=hx shr (20-j0);
+            end
           else
-            result:=tr;
-        float_round_to_zero:
-          result:=tr;
-        else
-          { needed for jvm: result must be initialized on all paths }
-          result:=0;
-      end;
+            result:=(int64(hx) shl (j0-20)) or (float64low(d) shr (52-j0));
+        end;
+      if sx<>0 then
+        result:=-result;
     end;
 {$endif FPC_SYSTEM_HAS_ROUND}