0

PowerShell processes the following commands with interesting outcomes. Hoping that a smart guy can explain why.

No problems here:

PS C:\> [float]"4.2"
4.2
PS C:\> [double]"4.2"
4.2

But the moment you do bit of arithmetic, then things get interesting:

PS C:\> [float]"4.2" + 3
7.19999980926514
PS C:\> [double]"4.2" + 3
7.2
Jack
  • 16,506
  • 19
  • 100
  • 167
  • 5
    Possible duplicate of [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – user4003407 Aug 05 '18 at 01:21
  • Note that from-string conversion is incidental to the issue. You'll get the same results with number literals (which, with a decimal point present or if expressed in scientific notation, default to `[double]`); e.g., `[float] 4.2 + 3`. You can create `[decimal]` literals with suffix `d`; e.g., `4.2d + 3d`. – mklement0 Aug 05 '18 at 04:34
  • 1
    @PetSerAl: Please do not promiscuously close floating-point problems as a duplicate of [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken). The behavior in this question is not caused merely by floating-point rounding but specifically by (a) PowerShell’s conversion of `float` to `double` during arithmetic and (b) PowerShell’s default rules for formatting floating-point numbers for display. – Eric Postpischil Aug 05 '18 at 05:30

2 Answers2

7

This has something to do with the way binary floating point numbers ([float] and [double]), are represented in memory.

In order to represent numerical values that vary in range beyond what can consistently be stored in 32 or 64 bits of memory, floating point numbers are stored as a calculation in the form:

coefficient*base^exponent

For a 32-bit binary floating-point number (which is what [float] is), this allows for a precision of roughly 7.2 decimal points - and indeed you see that the accuracy becomes wobbly after 7 decimals in your first example.

The [double] takes up 64 bits of memory (hence the name double) and thus have much higher precision.

In any case, please use [decimal] for decimal arithmetic, since it always uses 10 as its base, rather than 2 (which, as the name implies, is what binary floating-point numbers use as their base).

[decimal]"4.2" + 3
mklement0
  • 382,024
  • 64
  • 607
  • 775
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
2

This is caused by two PowerShell behaviors:

To understand the details, we start by observing that, when “4.2” is properly converted to IEEE-754 basic binary floating-point with round-to-nearest, the result is exactly 4.19999980926513671875 for float and 4.20000000000000017763568394002504646778106689453125 for double.

To display the float result, PowerShell (in effect) formats it with seven decimal digits, producing “4.200000”, and then suppresses trailing zeros, producing “4.2”.

PowerShell does arithmetic using double, not float. When you add 3, the result is a double, not a float. The resulting value is 7.19999980926513671875, and, since this is a double, PowerShell formats it with the default double format. In this case, it formats it with 15 digits, producing “7.19999980926514”.

For [double]"4.2" + 3, on the other hand, the result is 7.20000000000000017763568394002504646778106689453125. When this is formatted with 15 digits, the result is “7.20000000000000”. Suppressing trailing zeros leaves “7.2”.

(Note that such simple results are not always the case after arithmetic. There can be situations where the addition causes some rounding of low bits, with the result that the computed result will not finish with trailing zeros when formatted with 15 digits.)

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312