Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/System.Management.Automation/engine/LanguagePrimitives.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,8 @@ public static class LanguagePrimitives
internal delegate void MemberSetValueError(SetValueException e);

internal const string OrderedAttribute = "ordered";
internal const string DoublePrecision = "G15";
internal const string SinglePrecision = "G7";

internal static void CreateMemberNotFoundError(PSObject pso, DictionaryEntry property, Type resultType)
{
Expand Down Expand Up @@ -3311,6 +3313,17 @@ private static string ConvertNumericToString(object valueToConvert,
try
{
// Ignore formatProvider here, the conversion should be culture invariant.
var numberFormat = CultureInfo.InvariantCulture.NumberFormat;
if (valueToConvert is double dbl)
{
return dbl.ToString(DoublePrecision, numberFormat);
}

if (valueToConvert is float sgl)
{
return sgl.ToString(SinglePrecision, numberFormat);
}

return (string)Convert.ChangeType(valueToConvert, resultType, CultureInfo.InvariantCulture.NumberFormat);
}
catch (Exception e)
Expand Down
64 changes: 40 additions & 24 deletions src/System.Management.Automation/engine/MshObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1269,23 +1269,13 @@ internal static string ToStringParser(ExecutionContext context, object obj, IFor
/// </exception>
internal static string ToString(ExecutionContext context, object obj, string separator, string format, IFormatProvider formatProvider, bool recurse, bool unravelEnumeratorOnRecurse)
{
PSObject mshObj = obj as PSObject;

#region plain object
if (mshObj == null)
bool TryFastTrackPrimitiveTypes(object value, out string str)
{
if (obj == null)
{
return string.Empty;
}

// Fast-track the primitive types...
Type objType = obj.GetType();
TypeCode code = objType.GetTypeCode();
switch (code)
switch (Convert.GetTypeCode(value))
{
case TypeCode.String:
return (string)obj;
str = (string)value;
break;
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
Expand All @@ -1294,20 +1284,40 @@ internal static string ToString(ExecutionContext context, object obj, string sep
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
return obj.ToString();
case TypeCode.DateTime:
DateTime dt = (DateTime)obj;
return dt.ToString(formatProvider);
case TypeCode.Decimal:
Decimal dec = (Decimal)obj;
return dec.ToString(formatProvider);
var formattable = (IFormattable)value;
str = formattable.ToString(format, formatProvider);
break;
case TypeCode.Double:
double dbl = (double)obj;
return dbl.ToString(formatProvider);

var dbl = (double)value;
str = dbl.ToString(format ?? LanguagePrimitives.DoublePrecision, formatProvider);
break;
case TypeCode.Single:
float sgl = (float)obj;
return sgl.ToString(formatProvider);
var sgl = (float)value;
str = sgl.ToString(format ?? LanguagePrimitives.SinglePrecision, formatProvider);
break;
default:
str = null;
return false;
}

return true;
}

PSObject mshObj = obj as PSObject;

#region plain object
if (mshObj == null)
{
if (obj == null)
{
return string.Empty;
}

if (TryFastTrackPrimitiveTypes(obj, out string objString))
{
return objString;
}

#region recurse
Expand Down Expand Up @@ -1482,6 +1492,12 @@ internal static string ToString(ExecutionContext context, object obj, string sep
// Since we don't have a brokered ToString and the enumerations were not necessary or failed
// we try the BaseObject's ToString
object baseObject = mshObj._immediateBaseObject;

if (TryFastTrackPrimitiveTypes(baseObject, out string baseObjString))
{
return baseObjString;
}

IFormattable msjObjFormattable = baseObject as IFormattable;
try
{
Expand Down
18 changes: 7 additions & 11 deletions test/powershell/Language/Interop/DotNet/DotNetAPI.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,24 @@
# Licensed under the MIT License.

Describe "DotNetAPI" -Tags "CI" {
$posh_E = 2.718281828459045
$posh_pi = 3.14159265358979

It "Should be able to use static .NET classes and get a constant" {
[System.Math]::E | Should -Match $posh_E.ToString()
[System.Math]::PI | Should -Match $posh_pi.ToString()
[System.Math]::E | Should -Be 2.718281828459045
[System.Math]::PI | Should -Be 3.141592653589793
}

It "Should be able to invoke a method" {
[System.Environment]::GetEnvironmentVariable("PATH") | Should -Be $env:PATH
[System.Environment]::GetEnvironmentVariable("PATH") | Should -Be $env:PATH
}

It "Should not require 'system' in front of static classes" {
[Environment]::CommandLine | Should -Be ([System.Environment]::CommandLine)

[Math]::E | Should -Be ([System.Math]::E)
[Environment]::CommandLine | Should -Be ([System.Environment]::CommandLine)
[Math]::E | Should -Be ([System.Math]::E)
}

It "Should be able to create a new instance of a .Net object" {
[System.Guid]$guidVal = [System.Guid]::NewGuid()

$guidVal | Should -BeOfType Guid
[System.Guid]$guidVal = [System.Guid]::NewGuid()
$guidVal | Should -BeOfType Guid
}

It "Should access types in System.Console" {
Expand Down
26 changes: 26 additions & 0 deletions test/powershell/Language/Parser/Conversions.Tests.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

Describe 'conversion syntax' -Tags "CI" {
# these test suite covers ([<type>]<expression>).<method>() syntax.
# it mixes two purposes: casting and super-class method calls.
Expand Down Expand Up @@ -518,3 +519,28 @@ Describe 'method conversion' -Tags 'CI' {
$Result | Should -BeNullOrEmpty
}
}

Describe 'float/double precision when converting to string' -Tags "CI" {
It "<SourceType>-to-[string] conversion in PowerShell should use the precision specifier <Format>" -TestCases @(
@{ SourceType = [double]; Format = "G15"; ValueScript = { 1.1 * 3 }; StringConversionResult = "3.3"; ToStringResult = "3.3000000000000003" }
@{ SourceType = [double]; Format = "G15"; ValueScript = { 1.1 * 6 }; StringConversionResult = "6.6"; ToStringResult = "6.6000000000000005" }
@{ SourceType = [double]; Format = "G15"; ValueScript = { [System.Math]::E }; StringConversionResult = [System.Math]::E.ToString("G15"); ToStringResult = [System.Math]::E.ToString() }
@{ SourceType = [double]; Format = "G15"; ValueScript = { [System.Math]::PI }; StringConversionResult = [System.Math]::PI.ToString("G15"); ToStringResult = [System.Math]::PI.ToString() }
@{ SourceType = [float]; Format = "G7"; ValueScript = { [float]$f = 1.1; ($f * 3).ToSingle([cultureinfo]::InvariantCulture) }; StringConversionResult = "3.3"; ToStringResult = "3.3000002" }
@{ SourceType = [float]; Format = "G7"; ValueScript = { [float]$f = 1.1; ($f * 6).ToSingle([cultureinfo]::InvariantCulture) }; StringConversionResult = "6.6"; ToStringResult = "6.6000004" }
@{ SourceType = [float]; Format = "G7"; ValueScript = { [float]::MaxValue }; StringConversionResult = [float]::MaxValue.ToString("G7"); ToStringResult = [float]::MaxValue.ToString() }
@{ SourceType = [float]; Format = "G7"; ValueScript = { [float]::MinValue }; StringConversionResult = [float]::MinValue.ToString("G7"); ToStringResult = [float]::MinValue.ToString() }
) {
param($SourceType, $ValueScript, $StringConversionResult, $ToStringResult)

$value = & $ValueScript
$value | Should -BeOfType $SourceType
$value.ToString() | Should -BeExactly $ToStringResult

$value -as [string] | Should -BeExactly $StringConversionResult
[string]$value | Should -BeExactly $StringConversionResult
[System.Management.Automation.LanguagePrimitives]::ConvertTo($value, [string]) | Should -BeExactly $StringConversionResult
"$value" | Should -BeExactly $StringConversionResult
$value | Out-String | ForEach-Object -MemberName Trim | Should -BeExactly $StringConversionResult
}
}
4 changes: 2 additions & 2 deletions test/powershell/Language/Parser/Parser.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -650,8 +650,8 @@ foo``u{2195}abc
@{ Script = "-6.5"; ExpectedValue = "-6.5"; ExpectedType = [double] }
@{ Script = "9.12"; ExpectedValue = "9.12"; ExpectedType = [double] }
@{ Script = ".01"; ExpectedValue = "0.01"; ExpectedType = [double] }
@{ Script = $([single]::MinValue); ExpectedValue = $([float]::MinValue).ToString(); ExpectedType = [double] }
@{ Script = $([float]::MaxValue); ExpectedValue = $([float]::MaxValue).ToString(); ExpectedType = [double] }
@{ Script = $([single]::MinValue); ExpectedValue = $([float]::MinValue).ToString("G7"); ExpectedType = [double] }
@{ Script = $([float]::MaxValue); ExpectedValue = $([float]::MaxValue).ToString("G7"); ExpectedType = [double] }
#Exponential
@{ Script = "0e0"; ExpectedValue = "0"; ExpectedType = [double] }
@{ Script = "0e1"; ExpectedValue = "0"; ExpectedType = [double] }
Expand Down