繁体   English   中英

对[math] :: round的相同输入将返回不同的结果

[英]Same input into [math]::round returns different results

我在PowerShell中解析文件。 我有这部分代码(带有调试写入)

$max_file_size = 0
$max_file_size = $value
Write-Host $max_file_size # DEBUG
$max_file_size = Convert-to-Bytes $max_file_size
Write-Host $max_file_size # DEBUG
[string]$max_file_size = [math]::round($max_file_size, 0)
Write-Host $max_file_size # DEBUG

我看到两个不同的行具有相同的数据。 第一遍返回以下结果:

8192.00000P
9223372036854775808.00000
9223372036854775808

第二遍返回此结果:

8192.00000P
9223372036854775808.00000
9.22337203685478E+18

笏?

Function Convert-to-Bytes
{
    #Pass in a value and convert to bytes.
    param ($value)

    If ($value -eq $Null)
    {
        $value = 0
    }

    #Convert case to upper.
    [string]$value = $value.ToString().ToUpper()

    #Strip off 'B' if in string.
    [string]$value = $value.TrimEnd("B")

    [decimal]$output = 0

    If ($value.Contains("K"))
    {
        $value = $value.TrimEnd("K")
        [decimal]$output = $value
        [decimal]$output = $output * 1KB
    }
    If ($value.Contains("M"))
    {
        $value = $value.TrimEnd("M")
        [decimal]$output = $value
        [decimal]$output = $output * 1MB
    }
    If ($value.Contains("G"))
    {
        $value = $value.TrimEnd("G")
        [decimal]$output = $value
        [decimal]$output = $output * 1GB
    }
    If ($value.Contains("T"))
    {
        $value = $value.TrimEnd("T")
        [decimal]$output = $value
        [decimal]$output = $output * 1TB
    }
    If ($value.Contains("P"))
    {
        $value = $value.TrimEnd("P")
        [decimal]$output = $value
        [decimal]$output = $output * 1PB
    }
    If ($value.Contains("yte"))
    {
        $value = $value.TrimEnd("yte")
        [decimal]$output = $value
        [decimal]$output = $output
    }
    Return $output
}

声明类型变量时,PowerShell会向其添加ArgumentTypeConverterAttribute ,因此所有对该变量的后续赋值都将转换为该类型。

PS> $Var1=$null
PS> [string]$Var2=$null
PS> Get-Variable Var?|ft Name,Attributes -AutoSize

Name Attributes
---- ----------
Var1 {}
Var2 {System.Management.Automation.ArgumentTypeConverterAttribute}


PS> $Var1=1
PS> $Var2=1
PS> $Var1.GetType().FullName
System.Int32
PS> $Var2.GetType().FullName
System.String

在第一次传递结束时,您将max_file_size声明为string

[string]$max_file_size = [math]::round($max_file_size, 0)

所以在第二遍中,你分配给它的任何东西都被转换为string

我在每个Write-Host步骤中放置行,如下所示添加一些类型信息:

$max_file_size.GetType().FullName

这样我每次都会知道这种类型。 在第一关

0 : System.Int32
8192.00000P : System.String
9223372036854775808.00000 : System.Decimal
9223372036854775808 : System.String

同一会话的第二次传球

0 : System.String  
#^^^ String here makes sense since last it was cast to string in the previous run.
8192.00000P : System.String
9223372036854775808.00000 : System.String 
#^^^ Here is does not make sense since it should be a decimal returned from the function like before
9.22337203685478E+18 : System.String

仍然可以处理,但是[math]::round($max_file_size, 0)在第二遍被赋予了不同的数据,一个字符串而不是一个十进制。 到底是为什么。 你也可以通过这种方式看到差异

PS C:\users\Cameron\Downloads> [math]::round("9223372036854775808.00000", 0)
9.22337203685478E+18

PS C:\users\Cameron\Downloads> [math]::round([decimal]"9223372036854775808.00000", 0)
9223372036854775808

解决方法

我知道将此行添加到代码的开头可以解决问题。 这样每次都执行相同的操作。

Remove-Variable max_file_size

也可以强制从函数返回小数。

[decimal]$max_file_size = Convert-to-Bytes $max_file_size

向前

这是一个问题而不是答案,但如果没有其他人来帮忙,我可以尽我所能来解决这个问题。

肉和土豆

暂时只想将此答案分开,因为这可能只是此问题的工作空间,除非其他人没有考虑答案。 所以我试图使用Trace-Command来查看我是否能弄清楚发生了什么。 很多信息可以解析,但为了让开始变得简单,我做了这个。

使用您的函数创建了一个ps1脚本,并将以下代码放置了两次

Trace-Command -name TypeConversion -Expression {
$value = "8192.00000P"
$max_file_size = 0
$max_file_size = $value
$max_file_size = Convert-to-Bytes $max_file_size
[string]$max_file_size = [math]::round($max_file_size, 0)
} -PSHost

放一行,这样我就知道第一遍完成的地方和第二遍的开始。 考虑从TypeConversion开始,因为它似乎与问题相同。 我有以下发现(我将仅显示差异。)请注意,所有代码行都以DEBUG: TypeConversion Information为前缀DEBUG: TypeConversion Information但为了简洁和可读性,它已在此处删除

第一遍有关于数学装配的一些信息,只有一次才有意义

: 0 :         Conversion to System.Type
: 0 :             Found "System.Math" in the loaded assemblies.

在那之后不久, $max_file_size = 0$max_file_size = $value出现了,我只在第二遍看到了这个

: 0 : Converting "0" to "System.String".
: 0 :     Converting numeric to string.
: 0 : Converting "8192.00000P" to "System.String".
: 0 :     Result type is assignable from value to convert's type

真正问题的结束是看起来像函数返回和相关的字符串赋值[string]$max_file_size = [math]::round($max_file_size, 0)

1.通过

: 0 : Converting "9223372036854775808.00000" to "System.Decimal".
: 0 :     Result type is assignable from value to convert's type
: 0 : Converting "9223372036854775808" to "System.String".
: 0 :     Converting numeric to string.

通过

: 0 : Converting "9223372036854775808.00000" to "System.Decimal".
: 0 :     Result type is assignable from value to convert's type
: 0 : Converting "9223372036854775808.00000" to "System.String".
: 0 :     Converting numeric to string.
: 0 : Converting to double or single.
: 0 : Converting "9.22337203685478E+18" to "System.String".
: 0 :     Converting numeric to string.

看起来数据会使您的自定义函数与“9223372036854775808.00000”相同,但在第二次传递中返回$max_file_size值转换为double,因为您可以使用此代码进行复制。

[double]"9223372036854775808.00000"
9.22337203685478E+18

我不是故意浪费任何时间,但这是这个问题临时空间的最佳位置。

感觉Trace-Command可能无法获取我需要的信息,因为它无法从方法round获取调试信息

情节变粗

如果我将返回变量更改为类似$another_max_file_size ,然后在各个多次传递之后检查$another_max_file_size$max_file_size的类型,我会发现$max_file_size像我们已经确定的那样转换为[System.String] ,并且$another_max_file_size保持为一个[System.Decimal]

所以我们现在知道问题的根源是从这一行分配函数的输出。 同样,不要认为返回值而是赋值操作本身。

$max_file_size = Convert-to-Bytes $max_file_size
#              ^ something is happening here. 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM