1

Something has changed with System.Str in Delphi XE2. The following procedure :

procedure someProcedure;
var 
  E:double;
  outString:string;
begin
  E:=-1.7E+308;
  Str(E:i:j, outString);
end;

Raises an access violation

(exception class $C0000005, access violation at 0x00407318: read of address 0x30303028)

in Delphi XE2 where 'i' and 'j' are whatever integers. This same code works fine in Delphi 2010 and returns outString = '-1.7E+0308'. Similar code is used in a few of the TurboPower Orpheus components and it causes the entire IDE to crash on a BEX error.

This is in Win7 64-bit. Any ideas?

Edit : extra info

This seems to happen only with large negative numbers. Str seems to generate long strings which break when they exceed ~130 characters.

Note : this does not break when using only width (where i = some width)

procedure someProcedure;
var 
  E:double;
  outString:string;
begin
  E:=-1.7E+308;
  Str(E:i, outString);
end;
J...
  • 30,968
  • 6
  • 66
  • 143
  • That's not a precision error. That's just the fact that -1.6E117 is not exactly representable in binary floating point format. – David Heffernan Feb 15 '12 at 17:28
  • As a workaround you probably can just test if it's smaller than 0, negate the sign and then prefix '-' to the string. – CodesInChaos Feb 15 '12 at 17:28
  • 2
    Actually XE2 is getting it right. `1.60000000000000002E+0117` is correct. See Rob Kennedy's most useful page: http://pages.cs.wisc.edu/~rkennedy/exact-float?number=-1.6e117 – David Heffernan Feb 15 '12 at 17:32
  • @J... No, but you are passing a double. When you write `-1.6e117` and assign that literal to a double, the compiler chooses the nearest double to that value. – David Heffernan Feb 15 '12 at 17:38
  • @J... The 32 bit compiler has not changed its handling of floating point. Are you using the 64 bit compiler? Your understanding of how Delphi handles floating point is a little off. When you say everything is done with extended precision internally, that's true for the calculations performed on the 8087 hardware. But when you store a value from 8087 to a double variable, then it inevitably is forced into 8 byte format. D2010 can't store -1.6e117 exactly because that's not possible. It just happens that Str formats it to make you think it is storing it exactly. – David Heffernan Feb 15 '12 at 18:00
  • @J... I'm not saving face. The Str you are using is compiled on the 32 bit compiler. That's clear because 32 bit code and 64 bit code cannot exist in the same process. Since we know that this code has changed sufficiently to throw AVs, it's not surprising that its behaviour has changed in other ways. It doesn't matter which way you slice it, -1.6e117 is not exactly representable. – David Heffernan Feb 15 '12 at 18:14
  • 1
    @J... No, 32 bit Delphi D2010, XE2 etc. will not retain `-1.6e+117` as an `Extended` value once you push it into a variable. The Delphi compiler does not perform floating point optimisations which would be needed for that to happen. Essentially the compiler would need to optimise the variable into a floating point register. It does not do that. The difference must be solely in `Str`. The basic handling of floating point arithmetic, for the 32 bit compiler, is unchanged between D2010 and XE2. – David Heffernan Feb 15 '12 at 18:50
  • You're right - D2010 does it too. I don't know where I got that from, I've been compiling so many variations of this using extendeds and doubles that I thought I'd done large width doubles in 2010 to compare against but I mustn't have. I must have done large decimal, small width... Anyway, I apologize for hastily misunderstanding you. I always thought Delphi optimized that way but it makes sense now that it doesn't. – J... Feb 15 '12 at 19:15
  • 2
    No worries. It's a tricky subject. It just happens to be a point of special interest to me because my day job involves numerical computation. And regressions and changes in the way the compiler behave are very important to me. I don't think the 32 bit compiler has changed much for years and years. – David Heffernan Feb 15 '12 at 19:26

1 Answers1

7

It's clearly a bug with the handling of large magnitude negative numbers. Positive numbers are handled fine. If you can intercept the calls to Str then you could make sure that you only ever call Str passing positive numbers and then prefix the - yourself.

I have submitted the bug to Quality Central: QC#103436.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I was adding the bit about large negatives as you were writing this. There is also a precision error which affects positive values as well. – J... Feb 15 '12 at 17:27
  • 3
    @J... There is no precision error. The number is not exactly representable. – David Heffernan Feb 15 '12 at 17:29
  • In any case, I'll QC it when I get a moment. Workarounds are pretty obvious but it seemed something which shouldn't be broken in the first place. – J... Feb 15 '12 at 18:01
  • 1
    Thanks for the QC - I'm never logged in to EDN and never remember my details. – J... Feb 15 '12 at 18:32
  • 1
    The problem is that some of the involved functions have been rewritten in pure pascal for XE2 and obviously they broke some stuff at least in System._Str2Ext (see also [QC#101603](http://qc.embarcadero.com/wc/qcmain.aspx?d=101603)) – Stefan Glienke Feb 16 '12 at 05:58