3

Im trying to compare two Double variables in Powershell. When the variable is over 2 digits (not counting precision) the equality test fails unexpectedly.

Am I missing something obvious here?

Here is the test script output:

PS C:\test> .\test.ps1
==========
TEST ONE
==========
Value of Double1: 336.1
Type of Double1: System.Double
-----
Value of Double2: 336.2
Type of Double2: System.Double
-----
Value of Double1+.1: 336.2
Type of Double1+.1: System.Double
-----
Does Double1 not equal Double2: True
Does Double1+.1 not equal Double2: True

==========
TEST TWO
==========

Value of Double3: 36.1
Type of Double3: System.Double
-----
Value of Double4: 36.2
Type of Double4: System.Double
-----
Value of Double3+.1: 36.2
Type of Double3+.1: System.Double
-----
Does Double3 not equal Double4: True
Does Double3+.1 not equal Double4: False

And here is the test script, notice the first test fails the test where I add .1 to the variable, but the second test passes.

##################################
# START TEST
##################################

$Double1 = 336.1
$Double2 = 336.2

write-host "=========="
write-host "TEST ONE"
write-host "=========="
write-host "Value of Double1: $Double1"
write-host "Type of Double1:" $Double1.GetType().FullName
write-host "-----"
write-host "Value of Double2: $Double2"
write-host "Type of Double2:" $Double2.GetType().FullName
write-host "-----"
write-host "Value of Double1+.1:" ($Double1+.1)
write-host "-----"
write-host "Does Double1 not equal Double2:" ($Double1 -ne $Double2)    
write-host "Does Double1+.1 not equal Double2:" (($Double1+.1) -ne $Double2)
write-host ""

write-host "=========="
write-host "TEST TWO"
write-host "=========="

$Double3 = 36.1
$Double4 = 36.2

write-host ""
write-host "Value of Double3: $Double3"
write-host "Type of Double3:" $Double3.GetType().FullName
write-host "-----"
write-host "Value of Double4: $Double4"
write-host "Type of Double4:" $Double4.GetType().FullName
write-host "-----"
write-host "Value of Double3+.1:" ($Double3+.1)
write-host "-----"
write-host "Does Double3 not equal Double4:" ($Double3 -ne $Double4)    
write-host "Does Double3+.1 not equal Double4:" (($Double3+.1) -ne $Double4)
write-host ""
ProfessionalAmateur
  • 4,447
  • 9
  • 46
  • 63
  • 4
    It's April 1st, floating points don't work today. – Brian Rasmussen Apr 01 '13 at 20:52
  • 1
    If you are working just with precise decimal values (as pennies or points in gymnastics) you can use type decimal. Take a look at differences here http://stackoverflow.com/questions/618535/what-is-the-difference-between-decimal-float-and-double-in-c – pero Apr 01 '13 at 21:17

2 Answers2

7

Note that this isn't an issue with Powershell, it's simply a problem that exists with floating point arithmetic. You can reproduce the same behavior in C#

double d = 336.1;
Console.WriteLine((d + .1) == 336.2); // False

The problem is that 336.1 + .1 is not actually equal to 336.2. You can prove this by dumping the raw bytes for the 2 values

 unsafe
 {
    double d1 = 336.1;
    double d2 = d + .1d;
    double d3 = 336.2;
    Console.WriteLine(*(long*)(&d2));
    Console.WriteLine(*(long*)(&d3));
 }

Prints

4644622109139743540
4644622109139743539

Notice that the last 2 bytes are off by 1 value. As to why this is the case someone who deals more with floating point arithmetic would have to say. In general though you should be careful when comparing floating point values. Instead of strict equality it's better to check that they are within a specific range

if (Math.Abs(d2 - d3) <= .01)) {
  ...
}
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • Wow, so am I just out of luck trying with a Double? – ProfessionalAmateur Apr 01 '13 at 20:32
  • @ProfessionalAmateur i'm pretty sure this is a stack precision issue. I'm checking with a coworker who has more familiarity with this – JaredPar Apr 01 '13 at 20:36
  • 3
    That's not really an issue with .NET either. It's an issue with any code that uses floating point numbers, e.g. in any language that uses IEEE 754. – Joey Apr 01 '13 at 20:47
  • Thanks, I knew floating point was finicky, but I didn't think it would be at such a small scale. Appreciate the input. – ProfessionalAmateur Apr 01 '13 at 20:57
  • Fascinating! Just tested this on Python, same phenomenon – JMK Apr 01 '13 at 21:03
  • 3
    Most accurate representation of 336.1 is 3.36100000000000022737367544323E2. And of 0.1 is 1.00000000000000005551115123126E-1. So when you see 0.1 in your code and it is a double remember that it is not actually that precise value but just an approximation. For more try http://www.binaryconvert.com/. So as Joey said it is a limitation of the FINITE (limited number of bits) binary representation of real numbers. – pero Apr 01 '13 at 21:10
  • 2
    Anything base 10 that ends in .1 is bad, because one tenth is a repeating value when converted to binary, just like one third is when converting to decimal. – Joel Coehoorn Apr 01 '13 at 23:32
3

Try typecasting your variables as [System.Decimal] How to properly compare doubles in powershell? http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Community
  • 1
  • 1
Neossian
  • 695
  • 4
  • 14