FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Bug with bigint multiplication · Issue #21341 · PowerShell/PowerShell · GitHub

Repository navigation

Bug with bigint multiplication #21341

Closed
Closed

Description

Prerequisites

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.5

Actual behavior

PS> [bigint]5 * [double]1.5
5

Error details

There was no error

Environment 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.0

Visuals

No response

Activity

  1. added
    Needs-TriageThe issue is new and needs to be triaged by a work group.
    on Mar 13, 2024
  2. mklement0 commented on Mar 13, 2024

    Contributor

    The bug boils down to this:

    • While performing arithmetic operations with other integer types in combination with a [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:

    [bigint] 5 * [double] 1.5

    is currently - unexpectedly - the same as:

    [bigint] 5 * [bigint] [double] 1.5

    Since [bigint] 1.5 (same as: [bigint] [double] 1.5, given that a number literal with a decimal point is implicitly a [double]) is 1, the result is 5n (aka [bigint] 5)

  3. mklement0 commented on Mar 14, 2024

    Contributor

    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.BigInteger and System.Double in arithmetic expressions, so that, for instance, double r = (System.Numerics.BigInteger) 1 * 1.2; fails.

  4. Tragen commented on Mar 14, 2024

    Author

    You are right with C#, I made a mistake there.
    But in PS, there should be an error also and not a wrong result.

  5. mklement0 commented on Mar 14, 2024

    Contributor

    there should be an error

    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:

    # 5n == [bigint] 5; 1.5 == [double] 1.5
    5n * 1.5 | % {  [pscustomobject] @{ Result = $_; ResultType = $_.GetType() } } 

    SHOULD be:

    Result ResultType
    ------ ----------
     7.500 System.Double
    

    But currently is:

    Result ResultType
    ------ ----------
         5 System.Numerics.BigInteger
    

    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]::NegativeInfinity

    # Try to cast a [bigint] that is too large to fit into [double] to a [double]
    [double] (Invoke-Expression ('9' * 1e3 + 'n'))
    # Ditto for a negative number
    [double] (iex ('-' + ('9' * 1e3 + 'n')))
  6. SeeminglyScience commented on Apr 1, 2024

    Contributor

    The Engine WG reviewed this and we've decided this is by design. If bigint is specified, it's expected that very large numbers will be involved. If you need decimal places, we'd recommend converting the bigint to double before the operation.

  7. added
    Resolution-Won't FixThe issue won't be fixed, possibly due to compatibility reason.
    and removed
    Needs-TriageThe issue is new and needs to be triaged by a work group.
    Resolution-Won't FixThe issue won't be fixed, possibly due to compatibility reason.
    on Apr 1, 2024
  8. Tragen commented on Apr 2, 2024

    Author

    How about a warning when using Set-StrictMode -Version Latest
    At least you would know about this. I can accept that it's not working.
    But failing without error or warning is not good.

  9. mklement0 commented on Apr 3, 2024

    Contributor

    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-StrictMode is 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 that Set-StrictMode hasn'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.

  10. Tragen commented on Apr 3, 2024

    Author

    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.

  11. SeeminglyScience commented on Apr 3, 2024

    Contributor
    • 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.

    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.

    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.

    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.

  12. Tragen commented on Apr 3, 2024

    Author

    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.

  13. SeeminglyScience commented on Apr 3, 2024

    Contributor

    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.

    In C# it just fails to compile, the example you had was double * int. See this SharpLab.io example.

  14. Tragen commented on Apr 3, 2024

    Author

    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.

  15. mklement0 commented on Apr 4, 2024

    Contributor

    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)

    more worried about loss of precision than hitting an actual limit.

    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.:

    ([int]::MaxValue + 1).GetType().Name # !! 'Double'

    To give a concrete example of how loss of precision can occur in practice:

    # !! -> $false, because [long]::MaxValue + 2 is coerced to [double], resulting in loss of precision
    # !! The equivalent operation is the following - note the difference in the last digit.
    # !!  9223372036854775809ul -eq 9223372036854775808ul
    9223372036854775807ul + 2 -eq [ulong] ([long]::MaxValue + 2)

    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:

  16. mklement0 commented on Apr 4, 2024

    Contributor

    Tragen, let me attempt a pragmatic summary (speaking in no official capacity whatsoever):

    GitHub
    GitHub is where people build software. More than 100 million people use GitHub to discover, fork, and contribute to over 420 million projects.
  17. microsoft-github-policy-service commented on Apr 5, 2024

    Contributor

    This issue has been marked as by-design and has not had any activity for 1 day. It has been closed for housekeeping purposes.

  18. microsoft-github-policy-service commented on Apr 5, 2024

    Contributor

    📣 Hey Tragen, how did we do? We would love to hear your feedback with the link below! 🗣️

    🔗 https://aka.ms/PSRepoFeedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Resolution-By DesignThe reported behavior is by design.WG-Enginecore PowerShell engine, interpreter, and runtimeWG-ReviewedA Working Group has reviewed this and made a recommendation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL