11

We have a .NET app that interfaces with SFDC via a web service. When we import the WSDL the resulting classes will use the .NET type double wherever SFDC entities use a double. The problem I see here is that .NET's double and SFDC's double are really very different types.

  • .NET: Double variables are stored as signed IEEE 64-bit (8-byte) double-precision floating-point numbers
  • SFDC: On the other hand, is a precise representation with scale and precision

I'm worried that the approximate representation of a .NET double will lead to conversion errors (e.g. 10,000 becomes 9,999.9999999...) when reading and writing to/from the SFDC APIs.

Has anyone else encountered this issue and resolved it? Any ideas? Best practices?

Thanks!

EDIT

Here is the flow as I see it and the "worry" points

code: [biz logic]    [SOAP wrapper] [SOAP msg]     [SFDC]
type: C# decimal ►    C# double ►   xsd:double ►   SF double 
                 ▲                             ▲
           loss of precision           loss of precision?
  • 1
    .NET and SFDC each has the common boundary of the SOAP transport to take care of this... does the xsd:double data type really choke? – bigassforce Mar 15 '13 at 16:58
  • @user320: No, it isn't choking but I'm worried about conversion issues like 10,000 becoming 9,999.9999999 in a financial calculation. And SOAP may handle things fine but the generated wrapper classes force us to use double. I suppose this is more of a client-side WSDL and API issue than as SFDC issue. – Paul Sasik Mar 15 '13 at 17:19
  • Interesting: The xsd:double type books.xmlschemata.org/relaxng/ch19-77065.html is an IEEE 64-bit floating-point number. So what does SFDC do to convert it to its own double type while avoiding loss of precision? Am I missing something? – Paul Sasik Mar 15 '13 at 17:25
  • The data type to use in SFDC data calculations involving currency done in Apex would be Decimal which is backed by Java BigDecimal, likely the equivalent of C# decimal (I'm not super familiar with C#). – Peter Knolle Mar 15 '13 at 17:44
  • @PeterKnolle: Yes, that's exactly what I am after. The problem is when we add a custom field to Opportunity for example the type spit out in the WSDL is double. I know that there is a currency type but we also have order totals etc. which need decimal precision. – Paul Sasik Mar 15 '13 at 17:49
8
+50

TL;DR

If your C# decimal has a large number of significant figures (approximately 18 or more) then they can be truncated when sent to Salesforce via the Partner API in a SOAP message.

Oddly, the same is not true of the SOAP representation Salesforce returns for values that has been set via Apex. You can receive back significantly more digits than will fit in a IEEE 754 double-precision binary floating-point.

What can be done about all this from the .NET side? As a starting point, I'd suggest pulling the applicable Scale and Precision from the Meta Data and rounding the value while you still have the decimal representation.


My understanding is that there are a few issues here:

  1. As a double is a binary floating-point number it isn't possible to accurately represent certain values, such as 0.1. What you really have is an approximation that is very close the actual number you want.
  2. If you perform arithmetic operations involving doubles the representation issues can produce unexpected results. E.g. 3.65d+0.05d != 3.7d. This is a particular problem after several operations as the errors accumulate. See Round-off error
  3. Due to (1) and (2) you shouldn't use a double for numbers that rely of exact representations, such as currency values.
  4. A .NET decimal is a 102 bit number (128 bits of storage) that uses base 10 rather than base 2. The additional precision and base 10 representation make it more suitable for currency values.
  5. The Salesforce web services transports decimal fields as IEEE 64-bit floating-point numbers in the SOAP message.

So while you have a C# decimal in .NET and a Salesforce decimal for the field it will be converted to a double representation in the SOAP message.

Point 2 shouldn't be an issue as hopefully you aren't performing any arithmetic on these double values, just using them to transport the value before converting them back to the decimal representation.

That just leaves the question of if the .NET decimal ► Soap double ► Salesforce decimal introduces any issues with the precision.

As a starting point, I'd suggest running a series of tests to gather some empirical data showing a conversion error. Try round tripping a few potentially problematic values.


E.g. I tried creating two custom fields on Account, one Currency(8,2) called TestCurrency and another Number(8,2) called TestNumber. I also used the existing AnnualRevenue Currency(18,0) field.

I set the value for all three fields to the explicit string "3699.9999999999995" via the partner WSDL and queried them. They all came back with the same value. So a double has no problems with this value.

When viewed via the Salesforce UI:

  • AnnualRevenue appears as "$3,700",
  • TestCurrency as "$3,700.00" and
  • TestNumber as "3,700.00".

When queried in apex they all have the value "3699.9999999999995"

So, while the Salesforce UI respects the scale and precision settings, the underlying data structure does not. I've seen this before with the Data Exports. You will see values that couldn't possibly have been entered via the Salesforce UI with the current scale and precision settings. They might have existed before a change to the field settings, been imported via the API or set via Apex after a calculation.


Note that, at least with the Partner WSDL, you need to convert the doubles to strings to assign to the sObject Any XmlElement. While the soapType might be xsddouble you can also use the meta data to figure out if the type is currency and also get the precision and scale that Salesforce is using. .NET may perform some rounding with converting a double to a string. E.g. The double 3699.9999999999995 will be "3700" if you call ToString() with no arguments.


One area where you will definitely lose precision is if the .NET decimal has a mantissa ( floating-point number significand) that exceeds the number of digits a double can represent. Assuming 28 digits for the decimal mantissa (there are a few cases where you can have 29) we could have a number like "1.123456789012345678901234567" as a perfectly valid C# decimal.

If you try and assign that to any Salesforce decimal field via the SOAP Partner API it will be truncated to "1.1234567890123457" as it passes through the SOAP message. The field will appear in Apex with the truncated value and via subsequent partner API SOQL queries.

However, if you were to do the assignment via anonymous apex you don't lose the precision.

Account accToUpdate = new Account(Id='0017000000LeV0S');
accToUpdate.AnnualRevenue = Decimal.valueOf('1.123456789012345678901234567');
update accToUpdate;

Account acc = [Select Id, AnnualRevenue from Account where Id = :accToUpdate.Id];
System.debug(acc.AnnualRevenue);

Excerpts from the Log (my emphasis):

13:42:38.026 (26356000)|VARIABLE_ASSIGNMENT|2|this.AnnualRevenue|1.123456789012345678901234567|0x52e6f401
13:42:38.080 (80857000)|VARIABLE_ASSIGNMENT|5|acc|{"serId":1,"value":{"Id":"0017000000LeV0SAAV","AnnualRevenue":1.123456789012345678901234567}}|0x2ebbfa0f
13:42:38.080 (80971000)|USER_DEBUG|[6]|DEBUG|1.123456789012345678901234567

Note how this time the full decimal value was maintained in Salesforce.

Now for the really odd part. If I query this Account via the partner API the full value comes back!

Full decimal value returned via SOAP double


When configuring the length and decimal places of a Currency field you are limited to a combined length of 18 digits. This seems like it is designed to limit the currency field values to what can be represented with a double (although I'm fairly certain their will be some problem values around double max and min values).

Error: The sum of the length and decimal places must be an integer less than or equal to 18

Currency Field Options

  • 1
    Impressive synopsis. ;-) You're making me thankful that our agency so far has only had to deal with US Dollars going back and forth, with no partial cents involved. ;-) (here's hoping I haven't jinxed my future...) – AMM Mar 18 '13 at 19:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.