相同的输入 [math]::round returns 不同的结果
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
我看到两行数据相同。第一遍returns这个结果:
8192.00000P
9223372036854775808.00000
9223372036854775808
第二遍returns这个结果:
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
}
我在每个 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
前进
问题多于答案,但如果没有其他人来帮忙,也许我可以尽我所能解决这个问题。
肉和土豆
现在只想将这个答案分开,因为这可能只是解决这个问题的工作space,除非其他人没有考虑就得到了答案。所以我想用 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
但为了简洁和可读性,它已在此处删除
第一遍有一些关于数学汇编的信息,它只出现一次,这很有意义
: 0 : Conversion to System.Type
: 0 : Found "System.Math" in the loaded assemblies.
在那之后不久,$max_file_size = 0
和 $max_file_size = $value
的代码出现了,我只在 second pass
中看到了这个
: 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
最后真正的问题是函数中的 return 和相关的字符串赋值 [string]$max_file_size = [math]::round($max_file_size, 0)
.
1.st 通过
: 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.
2.nd 通过
: 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]"9223372036854775808.00000"
9.22337203685478E+18
我不想浪费任何人的时间,但这是解决此问题的最佳场所 space。
感觉Trace-Command
可能得不到我需要的信息,可能是因为它无法从方法round
中获取调试信息
情节加厚
如果我将 return 变量更改为 $another_max_file_size
之类的变量,然后在多次传递后检查 $another_max_file_size
和 $max_file_size
的类型,我看到 $max_file_size
转换为 [System.String]
就像我们已经确定的那样 $another_max_file_size
保持为 [System.Decimal]
所以我们现在知道问题的根源是从这一行分配函数的输出。同样,不要认为 return 而是赋值操作本身。
$max_file_size = Convert-to-Bytes $max_file_size
# ^ something is happening here.
当您声明类型变量时,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
。
我正在 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
我看到两行数据相同。第一遍returns这个结果:
8192.00000P
9223372036854775808.00000
9223372036854775808
第二遍returns这个结果:
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
}
我在每个 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
前进
问题多于答案,但如果没有其他人来帮忙,也许我可以尽我所能解决这个问题。
肉和土豆
现在只想将这个答案分开,因为这可能只是解决这个问题的工作space,除非其他人没有考虑就得到了答案。所以我想用 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
但为了简洁和可读性,它已在此处删除
第一遍有一些关于数学汇编的信息,它只出现一次,这很有意义
: 0 : Conversion to System.Type
: 0 : Found "System.Math" in the loaded assemblies.
在那之后不久,$max_file_size = 0
和 $max_file_size = $value
的代码出现了,我只在 second pass
: 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
最后真正的问题是函数中的 return 和相关的字符串赋值 [string]$max_file_size = [math]::round($max_file_size, 0)
.
1.st 通过
: 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.
2.nd 通过
: 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]"9223372036854775808.00000"
9.22337203685478E+18
我不想浪费任何人的时间,但这是解决此问题的最佳场所 space。
感觉Trace-Command
可能得不到我需要的信息,可能是因为它无法从方法round
情节加厚
如果我将 return 变量更改为 $another_max_file_size
之类的变量,然后在多次传递后检查 $another_max_file_size
和 $max_file_size
的类型,我看到 $max_file_size
转换为 [System.String]
就像我们已经确定的那样 $another_max_file_size
保持为 [System.Decimal]
所以我们现在知道问题的根源是从这一行分配函数的输出。同样,不要认为 return 而是赋值操作本身。
$max_file_size = Convert-to-Bytes $max_file_size
# ^ something is happening here.
当您声明类型变量时,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
。