.NET Float Types

First lets examine the types of floats that .NET supports.

Name Byte
Single 4 7 -3.4×1038 to +3.4×1038
Double 8 15-16 +-5.0×10324 to +-1.7×10308
Decimal 16 28-29 (-7.9×1028 to 7.9×1028) / (100 to 1028)

Delphi Float Types

Name Byte
Approximate Positive Range
Single 4 7-8 1.5×10-45 to 3.4×1038
Double 8 15-16 5.0×10-324 to 1.7×10308
Extended (x86) 10 10-20 3.4×10-4932 to 1.1×104932
Extended (x64)

8 15-16 5.0×10-324 to 1.7×10308

Delphi also has a Currency type but it is a fixed decimal type, not a floating one. Since it has no matching equivalent in .NET, it is not used by CrossTalk.

CrossTalk Mappings

The type used in Delphi depends on the type in .NET. When CrossTalk generates the .pas wrapper, the following mappings are used.

.NET Delphi
Single Single
Double Double
Decimal Extended

Single and double appear equivalent in Delphi and .NET, however because Delphi extends all float literals and most math operations to extended, in code they do not act exactly the same. Delphi’s most precise Extended is much less capable than .NET’s Decimal, so in such cases some precision may be lost.

Because internal representations may vary, and also because of varying precisions (such as Decimal and Extended), CrossTalk passes float values as strings. This ensures that the proper data is passed at all times and that precision loss is handled by the respective languages directly. Since Delphi treats all float literals as Extended and extends Single and Double to Extended for string operations, this can cause some unintended consequences.

xMyClass is a .NET class, and SingleField is a .NET Single. One might expect that this code should execute without error, however the assertion will fail. The assertion fails because Single cannot store 1.22 accurately and stores it as 1.22000002861023. This is floating point error and well known by developers. However Delphi has an issue that extends it for comparison in the Assert, and thus tries to compare 1.22 to 1.22000002861023 which causes the Assert to fail. Float literals in Delphi cannot be type casted. The only solution is to implicitly cast by assigning to temporary variables.

The above code is purely Delphi and does not involve CrossTalk, however you can see that the behavior is the same. Thus, the issue is a Delphi one not a CrossTalk or .NET issue.

.NET does not have this issue because float literals and string operations are typed. CrossTalk could have mapped all .NET float types to Extended instead, but this would cause other issues and cause CrossTalk code to act differently than Delphi code.

When dealing with floating points in Delphi, especially Single, the developer must be careful with float literals.