Double Precision Handling in DICOM DS Attributes
Double Precision Settings
In DICOM standard, the DS (Decimal String) Value Representation stores numerical values as strings, limited to 16 bytes. This constraint means full double precision often cannot be preserved. DicomObjects provides two configuration settings to help manage this:
DicomGlobal.PreserveDoublePrecision
DicomGlobal.DoublePrecisionDSAttributes
These settings determine how closely double values are preserved when writing to DS attributes within DICOM’s constraints.
How These Settings Work Together
PreserveDoublePrecision | DoublePrecisionDSAttributes | Behaviour |
---|---|---|
false | anything | No double precision is preserved for DS attributes. |
true | empty | Double precision is preserved for all DS attributes. |
true | non-empty list | Double precision is preserved only for attributes in the list. |
Platform-Specific Behaviour
The behaviour of double precision handling in DS attributes differs depending on the version of the DicomObjects library used.
DicomObjects for .NET Framework (8.40 / 8.48)
- Throws DicomValidationException for strings > 16 bytes.
- Rounding may seem inconsistent with double inputs over 15–17 significant digits due to the precision limit of the double type. Values beyond this are auto-rounded by .NET runtime before DicomObjects receives them.
- Input value -0.0 is stored and displayed as “0”.
DicomObjects for .NET Core / .NET (Core 3 / 5 / 8 / NetStandard 2.0)
- Throws ValidationException instead of DicomValidationException for strings > 16 bytes.
- Rounds consistently when compared to .NET Framework.
- Input value -0.0 is stored and displayed as “-0”.
- Higher precision for byte[] inputs regardless of PreserveDoublePrecision, and for double inputs when PreserveDoublePrecision is false.
Data Processing Behaviour
Depending on the data type used to assign values to DS attributes, behaviour varies:
1. Assigning a double
When PreserveDoublePrecision = true
:
- The value is rounded or truncated to fit within 16 bytes.
- Scientific notation is avoided.
When PreserveDoublePrecision = false
:
- Scientific notation is used for very large or small numbers.
- Results are usually rounded to 7 to 8 significant digits. .NET Framework preserves 7 significant digits and .NET Core 3.0+ up to 8 except in special cases such as double.MaxValue double.MinValue or infinity.
Examples Results (PreserveDoublePrecision = true
)
Input | .NET Framework | Core 3+ |
---|---|---|
12345678.123456789 | "12345678.1234568" | |
-12345678.123456789 | "-12345678.123456" | |
0.00000000012345 | "0.00000000012345" | |
123456789 | "123456789" | |
-123456789 | "-123456789" | |
-0.012345678901234567 | "-0.0123456789012" | |
-123456789012345 | "-123456789012345" | |
12345678901234569 | "1234567890123456" | |
123456789012345696 | "1234567890123457" | "1234567890123456" |
123456789012345655 | "1234567890123456" | |
double.MaxValue / double.PositiveInfinity | "9999999999999999" | |
double.MinValue/ double.NegativeInfinity | "-999999999999999" | |
double.NaN | null | |
0 | "0" | |
-0.0 | "0" | "-0" |
Examples Results (PreserveDoublePrecision = false
)
Input | .NET Framework | Core 3+ |
---|---|---|
12345678.123456789 | "1.234568E+07" | "12345678" |
-12345678.123456789 | "-1.234568E+07" | "-12345678" |
0.00000000012345 | "1.2345E-10" | |
123456789 | "1.234568E+08" | "123456790" |
-123456789 | "-1.234568E+08" | "-123456790" |
12345678901234569 | "1.234568E+16" | "1.2345678E+16" |
-123456789012345 | "-1.234568E+14" | "-1.2345679E+14" |
1234.56789 | "1234.568" | "1234.5679" |
1.23456789111111 | "1.234568" | "1.2345679" |
-0.012345678901234567 | "-0.01234568" | "-0.012345679" |
0.0 | "0" | |
-0.0 | "0" | "-0" |
double.MaxValue / double.PositiveInfinity | "9999999999999999" | |
double.MinValue/ double.NegativeInfinity | "-999999999999999" | |
double.NaN | null |
2. Assigning a string
Precision settings and the .NET runtime version do not affect string inputs.
- Strings ≤ 16 bytes are accepted as-is.
- Strings > 16 bytes raise a
DicomValidationException
orValidationException
. - Scientific notation is allowed if it fits.
Example Results
Input | .NET Framework | Core 3+ |
---|---|---|
"12345678.123456789" | DicomValidationException | ValidationException |
"-12345678.123456789" | DicomValidationException | ValidationException |
"12345678901234569" | DicomValidationException | ValidationException |
"123456789" | "123456789" | |
"-123456789" | "-123456789" | |
"-123456789012345" | "-123456789012345" | |
"1234.56789" | "1234.56789" | |
"1.23456789111111" | "1.23456789111111" | |
"0.00000000012345" | "0.00000000012345" | |
"-0.0123456789012" | "-0.0123456789012" | |
"0.0" | "0.0" | |
"-0.0" | "-0.0" | |
"1.23e-4" | "1.23e-4" |
3. Assigning a byte[]
Precision settings do not affect byte array input. The final value stored in the attribute is mainly affected by how the double is converted to a byte array before assigning it to the attribute.
The following example results’ inputs are based on convert a double value to string using ToString(), then convert it to byte array. The rounding/truncation already happened in the .ToString() before assigning byte array to attribute.
Example Code
double val = 12345678.123456789;
string ss = val.ToString();
byte[] bytes = Encoding.ASCII.GetBytes(ss);
dataSet.Add(Keyword.PatientSize, val);
Examples Results
Input | .NET Framework | Core 3+ |
---|---|---|
12345678.123456789 | "12345678.1234568" | "12345678.12345679" |
-12345678.123456789 | "-12345678.1234568" | "-12345678.12345679" |
123456789 | "123456789" | |
-123456789 | "-123456789" | |
12345678901234569 | "1.23456789012346E+16" | "12345678901234568" |
-123456789012345 | "-123456789012345" | |
1234.56789 | "1234.56789" | |
1.23456789111111 | "1.23456789111111" | |
0.00000000012345 | "1.2345E-10" | |
-0.0123456789012 | "-0.0123456789012" | |
0.0 | "0" | |
-0.0 | "0" | "-0" |
double.MaxValue | "1.79769313486232E+308" | "1.7976931348623157E+308" |
double.MinValue | "-1.79769313486232E+308" | "-1.7976931348623157E+308" |
double.NaN | "NaN" |