Search code examples
powershellcsvnumbersmultiplication

Multiplying 2 values gives the original value but dividing the same values gives the right answer


I have 2 values that I can not get to multiply together and get the right answer. Here is my very simple code:

$data | ForEach-Object {
            $newMHV = ($_.'North' * $_.'Measured Heat Value') 
            $_.'North BTU' = $newMHV
            $_               # Output the modified row
            } | Export-Csv $AFw -NoTypeInformation

The first time through the loop $_.'North' = 1648 and $_.'Measured Heat Value' = 1.022 $newMHV should be 1684.xxx. $newMVH always shows 1648 on the first value then changes to the second value.

When I change the calculation to division, I get the correct value.

I have tried trimming the values, etc. but I cant find it. I may need to convert them to a numeric but why would it work when I divide the values?

Note: $data is a .csv file that I have imported.


Solution

  • The crucial pointer is in the comments:

    • Data imported from a CSV file is invariably [string]-typed.

    • Thus, you must ensure that (at least) your LHS operand is of type [double] (or another suitable numeric type) to make your calculation work as expected (PowerShell will coerce the RHS to [double] too, as needed):

    $newMHV = [double] $_.'North' * $_.'Measured Heat Value'
    

    As for what you tried:

    • The * operator also operates on strings: if the LHS is a [string] and the RHS is / can be coerced to a number, the string is "multiplied" (duplicated) by the RHS number; that is, if the number is $n, you get the concatenation of $n copies of the LHS string;[1] e.g.:

      # -> 'abcabc'
      'abc' * 2 
      
      • In your case, the RHS, '1.022' didn't represent an integer, which means that PowerShell implicitly did the following: [int] '1.022' - which yields 1; therefore, your "calculation" was ultimately the equivalent of the following, which amounted to a virtual no-op:

        '1648' * 1 # -> '1648'
        
    • By contrast, the / operator operates only on numbers, so it coerces even [string]-typed operands to a number (which may situationally be an integer data type such as [int] or [double], as needed).

      # -> a [double] with value 2.5 - even though both operands are *strings*
      '10' / '4' 
      

    [1] It follows that if $n is 1, you get (an identical copy of) the string itself, and if it is 0, you get the empty string ('').