Li Tian Gong
Li Tian Gong

Reputation: 393

c# double value displayed as .9999998?

After further investigation, it all boils down to this:

(decimal)((object)my_4_decimal_place_double_value_20.9032)

after casting twice, it becomes 20.903199999999998


I have a double value, which is rounded to just 4 decimal points via Math.Round(...) the value is 20.9032

In my dev environment, it is displayed as is.

But in released environment, it is displayed as 20.903199999999998

There were no operation after Math.Round(...) but the value has been copied around and assigned.

How can this happen?

Updates: Data is not loaded from a DB.

returned value from Math.Round() is assigned to the original double varible.

Release and dev are the same architecture, if this information helps.

Upvotes: 1

Views: 671

Answers (2)

quadroid
quadroid

Reputation: 8940

A IEEE754 double precision floating point number can not represent 20.9032. The most accurate representation is 2.09031999999999982264853315428E1 and that is what you see in your output.

Do not format numbers with round instead use the string format of the double.ToString(string formatString) Method.

See msdn documentation of Double.ToString Method (String)

The difference between Release and Debug build may be some optimization that gets done for the release build, but this is way to detailed in my opinion. In my opinion the core issue is that you try to format a text output with a mathematical Operation. I'm sorry but i don't know what in detail creates the different behavior.

Upvotes: 2

Nicolas Voron
Nicolas Voron

Reputation: 2996

According to the CLR ECMA specification:

Storage locations for floating-point numbers (statics, array elements, and fields of classes) are of fixed size. The supported storage sizes are float32 and float64. Everywhere else (on the evaluation stack, as arguments, as return types, and as local variables) floating-point numbers are represented using an internal floating-point type. In each such instance, the nominal type of the variable or expression is either R4 or R8, but its value can be represented internally with additional range and/or precision. The size of the internal floating-point representation is implementation-dependent, can vary, and shall have precision at least as great as that of the variable or expression being represented. An implicit widening conversion to the internal representation from float32 or float64 is performed when those types are loaded from storage. The internal representation is typically the native size for the hardware, or as required for efficient implementation of an operation.

To translate, the IL generated will be the same (except that debug mode inserts nops in places to ensure a breakpoint is possible, it may also deliberately maintain a temporary variable that release mode deems unnecessary.)... but the JITter is less aggressive when dealing with an assembly marked as debug. Release builds tend to move more floating values into 80-bit registers; debug builds tend to read direct from 64-bit memory storage.

If you want a "precise" float number printing, use string.Substring(...) instead of Math.Round

Upvotes: 2

Related Questions