Browse Source

* fpc_val_real_shortstr: track amount of digits in mantissa and limit integer exponent values. This does not (yet) prevent floating-point overflows, but is a necessary step to prevent them.

git-svn-id: trunk@25549 -
sergei 12 years ago
parent
commit
d6a4e30f06
1 changed files with 48 additions and 3 deletions
  1. 48 3
      rtl/inc/sstrings.inc

+ 48 - 3
rtl/inc/sstrings.inc

@@ -1356,12 +1356,15 @@ end;
 const
 {$ifdef FPC_HAS_TYPE_EXTENDED}
   valmaxexpnorm=4932;
+  mantissabits=64;
 {$else}
 {$ifdef FPC_HAS_TYPE_DOUBLE}
   valmaxexpnorm=308;
+  mantissabits=53;
 {$else}
 {$ifdef FPC_HAS_TYPE_SINGLE}
   valmaxexpnorm=38;
+  mantissabits=24;
 {$else}
 {$error Unknown floating point precision }
 {$endif}
@@ -1537,6 +1540,10 @@ var
   exponent,
   expstart,
   decpoint : SizeInt;
+  nint,
+  nlz,
+  explimit,
+  explastdigit: SizeInt;
 begin
   fpc_Val_Real_ShortStr:=0.0;
   code:=1;
@@ -1544,6 +1551,8 @@ begin
   decpoint:=0;
   esign:=1;
   hd:=0.0;
+  nlz:=0;
+  nint:=0;
   sign:=1;
   while (code<=length(s)) and (s[code] in [' ',#9]) do
     inc(code);
@@ -1555,12 +1564,30 @@ begin
              inc(code);
            end;
     end;
+  { leading zeroes do not influence result, skip all but one of them }
+  expstart:=code;
+  while (code<Length(s)) and (s[code]='0') do
+    inc(code);
+  if (code>expstart) then
+    dec(code);
   expstart:=code;
   while (Code<=Length(s)) do
     begin
       case s[code] of
-        '0'..'9':
-          hd:=hd*10+(ord(s[code])-ord('0'));
+        '0':
+          begin
+            if (hd=0) then
+              inc(nlz,ord(decpoint<>0))
+            else
+              inc(nint,ord(decpoint=0));
+            hd:=hd*10;
+          end;
+        '1'..'9':
+          begin
+            if (decpoint=0) then
+              inc(nint);
+            hd:=hd*10+(ord(s[code])-ord('0'));
+          end;
         '.':
           if decpoint=0 then
             decpoint:=code
@@ -1591,9 +1618,27 @@ begin
                end;
         end;
       expstart:=code;
+      { Limit the exponent, accounting for digits in integer part of mantissa
+        and leading zeros in fractional part, e.g 100.0e306 = 1.0e308, etc. }
+      if (esign<0) then
+        explimit:=valmaxexpnorm+mantissabits-1+nint
+      else if (nint>0) then
+        explimit:=valmaxexpnorm+1-nint
+      else
+        explimit:=valmaxexpnorm+1+nlz;
+      explastdigit:=(explimit mod 10)+ord('0');
+      explimit:=explimit div 10;
       while (length(s)>=code) and (s[code] in ['0'..'9']) do
         begin
-          exponent:=exponent*10+(ord(s[code])-ord('0'));
+          if (exponent>explimit) or
+            ((exponent=explimit) and (ord(s[code])>explastdigit)) then
+            begin
+              { ignore exponent overflow for zero mantissa }
+              if hd<>0.0 then
+                exit;
+            end
+          else
+            exponent:=exponent*10+(ord(s[code])-ord('0'));
           inc(code);
         end;
       if code=expstart then