- Beyond Compare 4 for Windows has a problem converting binary dfm files having floating point values.
- Beyond Compare 4 for Mac cannot even convert binary dfm files: it errors with “Conversion Error”
The process of creating the example files below is using a Windows text editor and the Borland/CodeGear/Embarcadero/Idera supplied convert.exe
:
1. Manually create Example.dfm
(see text below)
2. copy /y Example.dfm Example.binary.dfm && convert.exe -i -b Example.binary.dfm
3. copy /y Example.binary.dfm Example.text.dfm && convert.exe -i -t Example.text.dfm
Example.dfm
content:
``` textobject MyObject: TMyObject
Left = 1
Top = 2
ValAsFloat = 100.000000000000000000
end
```
Beyond Compare (at least the Windows x64 Version 4.2.9 (build 23626); the same version on Mac refuses to convert binary dfm files) gets the conversion wrong and convert the binary to this text, then compares:
<pre>“` text
object MyObject: TMyObject
Left = 1
Top = 2
ValAsFloat = 100.00000000000000000
end
“`
The difference is hard to see, but the Beyond Compare converted text misses one zero at the end of the <code>ValAsFloat</code> line.
Steps to reproduce in Beyond Compare:
1. Load <code>Example.dfm</code> and <code>Example.text.dfm</code> in a Beyond Compare comparison: they should be the same.
2. Load <code>Example.binary.dfm</code> and <code>Example.text.dfm</code> in a Beyond Compare comparison: they should be the same, but are different.
</div>
</div>
—
<h4>Aaron</h4>
</div>
</div>
Hello,
Thanks for the report. We're investigating the difference in behavior between Windows and MacOS, which appears to be a limitation if using Doubles in forms on MacOS.
For the difference in precision, BC4's internal conversion is using the same code as (a slightly older) convert.exe. I have an older version of Delphi on my own test machine and tried a few other conversions, such as adding an additional “1” or altering the last 0 as a “1” in your example, and the command line convert fails to catch both of these at this precision level. I had to insert a value of “100.0000000000001” before finding a level that convert.exe handled. What behavior do you see if you use the command line steps above but insert values?
Assuming your command line shows better behavior, as a workaround, you can also create a new File Format which uses your above command line as the conversion utility, instead of the built-in dfm conversion. We have a KB article for defining any command line utility example (for resx) here:
http://www.scootersoftware.com/suppo…rnalconversion
This could use a convert.bat of something like
<pre class="bbcode_code">xcopy %1 %2* /Y
convert.exe -i -t %2</pre>
</div>
</div>
</div>
—
<h4>jeroenp</h4>
</div>
</div>
I have seen many floating point values going wrong. All of them have to do with Beyond Compare missing the extra zero, while Delphi adding it.
What Convert.exe versions to you have access to?
Do you have time to get an overview of the various Convert.exe to text conversions and their differences?
I do not have a machine with sufficient Convert.exe versions, only one with copies of most of the RTL source code (I seem to be missing Delphi 2005, and do not have access to Delphi 10.3 Rio).
Looking at my own https://bitbucket.org/jeroenp/wiert.me/src/tip/Native/Delphi/Apps/Console/DfmTools/ConvertDfmToText/ConvertDfmToText.dprconversion tool that I use for mass conversions, the core code for the Double to Text conversion is in this method of System.Classes.pas (Delphi 10.1 Berlin):
<pre class="bbcode_code">“` pascal
procedure ConvertValue;
…
begin
case Reader.NextValue of
…
vaExtended, vaDouble:
WriteAsciiStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
vaSingle:
WriteAsciiStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
vaCurrency:
WriteAsciiStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
vaDate:
WriteAsciiStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
…
end;
“`
</div>
or Delphi 1:
<pre class="bbcode_code">“` pascal
procedure ConvertValue;
begin
case Reader.ReadValue of
…
vaExtended:
begin
Reader.Read(Ext, SizeOf(Ext));
WriteStr(FloatToStr(Ext));
end;
…
end;
“`
</div>
In the next reply is what a quick search revealed: at about Delphi 7, 18 floating point format with 18 decimal digits was introduced.
Maybe it makes sense to incorporate two Dfm conversions in Beyond Compare: legacy (current behaviour) and mainstream (Delphi 7 and up behaviour).
</div>
</div>
—
<h4>jeroenp</h4>
</div>
</div>
From my archives:
<pre class="bbcode_code">“` text
C:\Delphi-RTL-VCL-FMX-Sources\Library-Sources>grep -ind “WriteAsciiStr\(FloatToStr” *Classes.pas
D10.1Berlin-Professional-D24-RadStudio-18.0\source.24.0.25048.9432\rtl\common\System.Classes.pas
13694 WriteAsciiStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
13696 WriteAsciiStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
13698 WriteAsciiStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
13700 WriteAsciiStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
D10.2Tokyo-Professional-D25-RadStudio-19.0\source.25.0.29039.2004\rtl\common\System.Classes.pas
13938 WriteAsciiStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
13940 WriteAsciiStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
13942 WriteAsciiStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
13944 WriteAsciiStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
D10Seattle-Enterprise-D23-RadStudio-17.0\source.23.0.20618.2753\rtl\common\System.Classes.pas
13346 WriteAsciiStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
13348 WriteAsciiStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
13350 WriteAsciiStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
13352 WriteAsciiStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
D10Seattle-Enterprise-D23-RadStudio-17.0\source.23.0.21418.4207\rtl\common\System.Classes.pas
13351 WriteAsciiStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
13353 WriteAsciiStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
13355 WriteAsciiStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
13357 WriteAsciiStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
DXE3-Enterprise-D17-RadStudio-10.0\source\rtl\common\System.Classes.pas
13589 WriteAsciiStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
13591 WriteAsciiStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
13593 WriteAsciiStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
13595 WriteAsciiStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
DXE4-Enterprise-D18-RadStudio-11.0\source\rtl\common\System.Classes.pas
13573 WriteAsciiStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
13575 WriteAsciiStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
13577 WriteAsciiStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
13579 WriteAsciiStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
DXE5-Enterprise-D19-RadStudio-12.0\source\rtl\common\System.Classes.pas
13561 WriteAsciiStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
13563 WriteAsciiStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
13565 WriteAsciiStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
13567 WriteAsciiStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
DXE6-Enterprise-D20-RadStudio-14.0\source\rtl\common\System.Classes.pas
13589 WriteAsciiStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
13591 WriteAsciiStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
13593 WriteAsciiStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
13595 WriteAsciiStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
DXE7-Enterprise-D21-RadStudio-15.0\source\rtl\common\System.Classes.pas
13149 WriteAsciiStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
13151 WriteAsciiStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
13153 WriteAsciiStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
13155 WriteAsciiStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
DXE8-Enterprise-D22-RadStudio-16.0\source\rtl\common\System.Classes.pas
13343 WriteAsciiStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
13345 WriteAsciiStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
13347 WriteAsciiStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
13349 WriteAsciiStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
RS19.0-source.bad\rtl\common\System.Classes.pas
13938 WriteAsciiStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
13940 WriteAsciiStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
13942 WriteAsciiStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
13944 WriteAsciiStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
C:\Delphi-RTL-VCL-FMX-Sources\Library-Sources>grep -ind “WriteStr\(FloatToStr” *Classes.pas
D1-CS-1.0\SOURCE\VCL\CLASSES.PAS
3423 WriteStr(FloatToStr(Ext));
D2-CS-2.0\SOURCE\VCL\CLASSES.PAS
4812 WriteStr(FloatToStr(Reader.ReadFloat));
D2006-Enterprise-D10-BDS-4.0\source\dotNet\rtl\Borland.Vcl.Classes.pas
11214 WriteStr(FloatToStr(Reader.ReadFloat));
11216 WriteStr(FloatToStr(Reader.ReadSingle) + ‘s’);
11220 WriteStr(FloatToStr(Reader.ReadDate) + ‘d’);
D2006-Enterprise-D10-BDS-4.0\source\Win32\rtl\common\Classes.pas
9091 WriteStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18));
9093 WriteStr(FloatToStr(Reader.ReadSingle) + ‘s’);
9095 WriteStr(FloatToStr(Reader.ReadCurrency * 10000) + ‘c’);
9097 WriteStr(FloatToStr(Reader.ReadDate) + ‘d’);
D2007-Enterprise-D11-RadStudio-5.0\source\Win32\rtl\common\Classes.pas
9102 WriteStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18));
9104 WriteStr(FloatToStr(Reader.ReadSingle) + ‘s’);
9106 WriteStr(FloatToStr(Reader.ReadCurrency * 10000) + ‘c’);
9108 WriteStr(FloatToStr(Reader.ReadDate) + ‘d’);
D2009-Enterprise-D12-RadStudio-6.0\source\Win32\rtl\common\Classes.pas
9967 WriteStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18));
9969 WriteStr(FloatToStr(Reader.ReadSingle) + ‘s’);
9971 WriteStr(FloatToStr(Reader.ReadCurrency * 10000) + ‘c’);
9973 WriteStr(FloatToStr(Reader.ReadDate) + ‘d’);
D2010-Enterprise-D14-RadStudio-7.0\source\Win32\rtl\common\Classes.pas
10087 WriteStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18));
10089 WriteStr(FloatToStr(Reader.ReadSingle) + ‘s’);
10091 WriteStr(FloatToStr(Reader.ReadCurrency * 10000) + ‘c’);
10093 WriteStr(FloatToStr(Reader.ReadDate) + ‘d’);
D3-CS\SOURCE\VCL\classes.pas
5430 WriteStr(FloatToStr(Reader.ReadFloat));
D4-CS\SOURCE\VCL\CLASSES.PAS
6239 WriteStr(FloatToStr(Reader.ReadFloat));
6241 WriteStr(FloatToStr(Reader.ReadSingle) + ‘s’);
6243 WriteStr(FloatToStr(Reader.ReadCurrency * 10000) + ‘c’);
6245 WriteStr(FloatToStr(Reader.ReadDate) + ‘d’);
D5-Enterprise\Source\Vcl\classes.pas
6645 WriteStr(FloatToStr(Reader.ReadFloat));
6647 WriteStr(FloatToStr(Reader.ReadSingle) + ‘s’);
6649 WriteStr(FloatToStr(Reader.ReadCurrency * 10000) + ‘c’);
6651 WriteStr(FloatToStr(Reader.ReadDate) + ‘d’);
D6-Enterprise\Source\Rtl\Common\Classes.pas
8345 WriteStr(FloatToStr(Reader.ReadFloat));
8347 WriteStr(FloatToStr(Reader.ReadSingle) + ‘s’);
8349 WriteStr(FloatToStr(Reader.ReadCurrency * 10000) + ‘c’);
8351 WriteStr(FloatToStr(Reader.ReadDate) + ‘d’);
D7.01.Architect\Source\Rtl\Common\Classes.pas
8613 WriteStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18));
8615 WriteStr(FloatToStr(Reader.ReadSingle) + ‘s’);
8617 WriteStr(FloatToStr(Reader.ReadCurrency * 10000) + ‘c’);
8619 WriteStr(FloatToStr(Reader.ReadDate) + ‘d’);
DXE-Enterprise-D15-RadStudio-8.0\source.15.0.3953.3571\rtl\common\Classes.pas
10438 WriteStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
10440 WriteStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
10442 WriteStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
10444 WriteStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
DXE2-Enterprise-D16-RadStudio-9.0\source\rtl\common\System.Classes.pas
10805 WriteStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
10807 WriteStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
10809 WriteStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
10811 WriteStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
DXE2-Enterprise-D16-RadStudio-9.0\source.U1\rtl\common\System.Classes.pas
10805 WriteStr(FloatToStrF(Reader.ReadFloat, ffFixed, 16, 18, LFormatSettings));
10807 WriteStr(FloatToStr(Reader.ReadSingle, LFormatSettings) + ‘s’);
10809 WriteStr(FloatToStr(Reader.ReadCurrency * 10000, LFormatSettings) + ‘c’);
10811 WriteStr(FloatToStr(Reader.ReadDate, LFormatSettings) + ‘d’);
“`
—
jeroenp
In the mean time, could you add another example like http://www.scootersoftware.com/suppo…rnalconversion (which talks about parameters %s and %t) for your convert.exe mockup (which talks about %1 and %2*).
That would very much help me as I’m pressed for time (I have to assist someone I am responsible for and is close to me that is suffering from long term mental abuse which takes enough time and energy that I only do programming work 3 days a week).
—
Aaron
Hello,
Sorry I wasn’t clear. The example above is for a “convert.bat” is just a simple bat file with those two lines. This is an override to use your other version of Convert.exe instead of BC4’s to get the exact results you need from the command line app. By defining a new External Conversion, this then allows you to use any Convert.exe you have installed (instead of using the internal code for BC4). The BC4 File Format -> Text Format (a new custom Format or an edited one) then uses the .bat file as the External Conversion, which then calls to your copy (or any copy) of Convert.exe.
If you would like to compile and use your custom conversion utility, that is great, too. The above example KB article uses .resx as the example program as the walkthrough example, but any utility can be inserted as a custom conversion for for a custom File Format (either specific versions of Convert.exe or your own program).
My own test convert.exe looks like version 7.0. My own tests were printing the same number of “0”‘s, but I found the significant digits were not respected and it was dropping data (completely independent of BC4; only using the command line Convert.exe process). One of our developers was looking into it with a newer version of Delphi, but was still seeing a difference in floating point representation. It doesn’t appear to be a quick fix, but I’ll add your investigative notes in case it helps.
—
Aaron
Helpers\convert.bat %s %t
with Disable Editing enabled.I then copied/pasted the convert.bat into the %AppData%\Scooter Software\Beyond Compare 4\Helpers\ directory, where the other File Format helper files usually live.
—
jeroenp
Thanks for that. I will give it a try later.
I know Delphi has problems loosing digits in floating point values. But sometimes those digits are not lost, so that’s why they switched to one more digit around Delphi 7.
—
–jeroen
This entry was posted on 2021/06/17 at 12:00 and is filed under Beyond Compare, Development, Power User, Software Development. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
This site uses Akismet to reduce spam. Learn how your comment data is processed.
-
Follow
Following
Already have a WordPress.com account? Log in now.
Leave a Reply