Repository navigation
Closed
Labels
Resolution-By DesignThe reported behavior is by design.The reported behavior is by design.WG-Enginecore PowerShell engine, interpreter, and runtimecore PowerShell engine, interpreter, and runtimeWG-ReviewedA Working Group has reviewed this and made a recommendationA Working Group has reviewed this and made a recommendation
Description
Prerequisites
- Write a descriptive title.
- Make sure you are able to repro it on the latest released version
- Search the existing issues.
- Refer to the FAQ.
- Refer to Differences between Windows PowerShell 5.1 and PowerShell.
Steps to reproduce
Write-Host "int: $([int]5 * [double]1.5)"
Write-Host "bigint: $([bigint]5 * [double]1.5)"
prints
int: 7.5
bigint: 5
using c# directly in PS prints the correct result
Add-Type -TypeDefinition @"
using System;
namespace DemoCode
{
public class DemoClass
{
public static void Test()
{
int i = 5;
System.Numerics.BigInteger bi = 5;
double d = 1.5;
double r = i * d;
Console.WriteLine("bigint c#: " + r.ToString());
}
}
}
"@
[DemoCode.DemoClass]::Test()
prints
bigint c#: 7.5
Why is the multiplication directly in PS truncating the double?
It doesn't matter if I use 1.1, 1.5 or 1.9, the double gets truncated and then multiplied.
This bug happens in PowerShell 74.1 and 5
Expected behavior
PS> [bigint]5 * [double]1.5
7.5Actual behavior
PS> [bigint]5 * [double]1.5
5Error details
There was no errorEnvironment data
PS> $PSVersionTable
Name Value
---- -----
PSVersion 7.4.1
PSEdition Core
GitCommitId 7.4.1
OS Microsoft Windows 10.0.22631
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0Visuals
No response
Reacted by Michael Klement
Metadata
Metadata
Assignees
Labels
Resolution-By DesignThe reported behavior is by design.The reported behavior is by design.WG-Enginecore PowerShell engine, interpreter, and runtimecore PowerShell engine, interpreter, and runtimeWG-ReviewedA Working Group has reviewed this and made a recommendationA Working Group has reviewed this and made a recommendation
Activity
mklement0 commentedon Mar 13, 2024
The bug boils down to this:
[double](or the rarely used[float]) yield a[double]-typed result, with[bigint], specifically, the respective other operand is forced to[bigint]too, and the result is also a[bigint].In other words:
is currently - unexpectedly - the same as:
Since
[bigint] 1.5(same as:[bigint] [double] 1.5, given that a number literal with a decimal point is implicitly a[double]) is1, the result is5n(aka[bigint] 5)mklement0 commentedon Mar 14, 2024
rhubarb-geek-nz:
Fundamentally, C# plays by different rules than PowerShell, so differing behavior isn't in and of itself relevant.
For that reason, my comment focused on how the observed behavior is inconsistent in the realm of PowerShell - and that's what the conversation should focus on.
As an - inconsequential - aside: C# prevents mixing
System.Numerics.BigIntegerandSystem.Doublein arithmetic expressions, so that, for instance,double r = (System.Numerics.BigInteger) 1 * 1.2;fails.Tragen commentedon Mar 14, 2024
You are right with C#, I made a mistake there.
But in PS, there should be an error also and not a wrong result.
mklement0 commentedon Mar 14, 2024
I haven't looked at any technical challenges, but ideally the - PowerShell-idiomatic - behavior would be:
Automatically convert to
[double], because that's what happens with all other integer types ([sbyte],[byte],[int16],[uint16],[int],[uint],[long],[uint64]).Thus:
SHOULD be:
But currently is:
However, the open-ended nature of
[bigint]theoretically makes it possibly for a given instance not to fit into a[double]- something that cannot happen with the other integer types.In that case, it would make sense to exhibit the same behavior as when trying too cast a too-large / too-small
[bigint]to a[double], which means:[double]::PositiveInfinity/[double]::NegativeInfinitySeeminglyScience commentedon Apr 1, 2024
The Engine WG reviewed this and we've decided this is by design. If
bigintis specified, it's expected that very large numbers will be involved. If you need decimal places, we'd recommend converting thebiginttodoublebefore the operation.Tragen commentedon Apr 2, 2024
How about a warning when using
Set-StrictMode -Version LatestAt least you would know about this. I can accept that it's not working.
But failing without error or warning is not good.
mklement0 commentedon Apr 3, 2024
Tragen, speaking in no official capacity whatsoever:
I disagree with the WG decision: coercion to
[double]- even if it situationally results in[double]::PositiveInfinity/[double]::NegativeInfinity- amounts to behavior that is consistent with arithmetic with other types; receiving either of the two results allows you to infer what happened. Carving out an exception for[bigint]is a confusing exception that is likely to trip up users.Set-StrictModeis problematic in general, given PowerShell's dynamic scoping: it affects not just the current scope, but also all descendent scopes, and therefore requires careful managing; note thatSet-StrictModehasn't seen updates since-Version 3.Given the WG decision:
The behavior should be documented: however, going by past behavior, this likely won't happen until a (non-team) user takes the initiative to ask that the Type conversion to accommodate result part of the docs be amended.
The best you can hope for is implementation of a PSSA rule, so that, for instance you'd get a design-time warning in Visual Studio Code - again, this will likely require user initiative.
Tragen commentedon Apr 3, 2024
I think also the decision is wrong.
You force all users to start searching why it isn't working until you find the documentation or the this bug.
It's unnecessary at makes more work.
SeeminglyScience commentedon Apr 3, 2024
I would be more worried about loss of precision than hitting an actual limit. Neither choice is wrong here, we just think it's more likely to need precision if you've already specified a bigint. Also helps that it's the existing behavior at least as far back as 5.1 so no breaks.
Keep in mind that this would also apply if we changed it and someone was instead expecting precision. Short of becoming a strict language like C# where it would instead simply fail to compile, that isn't really avoidable.
Tragen commentedon Apr 3, 2024
That's the reason why I wanted a strict mode with more warnings.
For me I like it when it fails fast because it's wrong.
And this is clearly wrong and it's also wrong because it works differently in C#.
I would think that PowerShell is .Net and behaves the same way like C# so different behavior is just wrong for me imho.
SeeminglyScience commentedon Apr 3, 2024
In C# it just fails to compile, the example you had was
double * int. See this SharpLab.io example.Tragen commentedon Apr 3, 2024
I know. And I expect that to happen in PowerShell as well.
I would know much faster to use long or int or double or cast it if necessary.
mklement0 commentedon Apr 4, 2024
Tragen, it was never PowerShell's goal to be C#-compatible, and it intentionally deviates from C# in several ways, notably with respect to PowerShell's very flexible implicit type conversions:
Typically, these conversions are helpful and work as one would intuitively expect - pitfalls notwithstanding - and are a better fit for a "looser" shell scripting language vs. a compiled language such as C#.
In the context of PowerShell's existing behavior with integer type operands - coercion to
[double]when an operand of that type is involved - I would have expected[bigint]to be treated the same way: it would make for consistent behavior in a PowerShell context.As Patrick Meinecke (@SeeminglyScience) points out, the behavior of unexpectedly forcing
[double]operands to[bigint]- an inconsistency in the realm of PowerShell - goes back to Windows PowerShell, which would make changing the behavior a more serious breaking change than I originally thought.Yet, breaking changes are still acceptable if they fall into bucket 3: a breaking change considered to be beneficial while deemed to be unlikely to impact existing code.
However, determining whether something falls into bucket 3 would require extensive analysis of real-world scripts.
Patrick Meinecke (@SeeminglyScience)
While I can appreciate that perspective, note that loss of precision has always been and continues to be an ever-present danger in PowerShell, due to the implicit widening to
[double]that occurs whenever an[int]/[long]operand is involved in an operation that exceeds the range of these types (smaller integer types are in effect treated like[int]; e.g.:To give a concrete example of how loss of precision can occur in practice:
By contrast, number literals more helpfully widen their types from
[int]->[long]->[decimal]->[double].Suggesting that, for integer literals,
[decimal]transition to[bigint]instead of[double], is the subject of:mklement0 commentedon Apr 4, 2024
Tragen, let me attempt a pragmatic summary (speaking in no official capacity whatsoever):
It isn't PowerShell's goal to be consistent with C# - that won't change.
The WG decision is justifiable, especially from a backward-compatibility perspective.
The documentation should be amended to describe the existing
[bigint]behavior; I've created an issue to bring that about:I don't think that a strict-mode warning will be implemented; the next best thing (though not equivalent) is to get a design-time warning, via implementation of a PSSA rule, which notably would be surfaced via the PowerShell extension in Visual Studio Code.
microsoft-github-policy-service commentedon Apr 5, 2024
This issue has been marked as by-design and has not had any activity for 1 day. It has been closed for housekeeping purposes.
microsoft-github-policy-service commentedon Apr 5, 2024
📣 Hey Tragen, how did we do? We would love to hear your feedback with the link below! 🗣️
🔗 https://aka.ms/PSRepoFeedback