String-to-number coercion differs from Number() for uppercase base prefixes and binary strings
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 6.3k
- Forks
- 1.4k
- Avg merge
- 1d 16h
- Merged PRs (30d)
- 24
Description
Describe the bug
String-to-number coercion in comparisons treats uppercase hexadecimal/octal prefixes and binary prefixes differently from JavaScript's Number(), which ExpressionUtility.ParseNumber names as its reference. Lowercase hex/octal controls work.
This was measured with quoted string operands, not numeric literal parsing, on September 15, 2026:
| Actions expression | Actions result | JavaScript equivalent |
|---|---|---|
'0x1F' == 31 |
true |
Number('0x1F') === 31: true |
'0X1F' == 31 |
false |
Number('0X1F') === 31: true |
'0o17' == 15 |
true |
Number('0o17') === 15: true |
'0O17' == 15 |
false |
Number('0O17') === 15: true |
'0b101' == 5 |
false |
Number('0b101') === 5: true |
'1e309' == Infinity |
true |
Number('1e309') === Infinity: true |
The overflow control is included because rejection of the literal 1e309 does not imply rejection of the string '1e309' during coercion.
To reproduce
Copy the exact executed workflow into a repository you control, register it on the default branch and dispatch it. It requires no permissions, secrets, checkout or third-party actions. All six comparisons and Node equivalents execute in one job.
Example:
env:
RESULT: ${{ '0X1F' == 31 }}
run: printf 'result=%s\n' "$RESULT"
Output:
result=false
Expected behavior
Either align these conversions with Number(), or document the intentionally supported subset and its differences so workflow and linter authors can implement the same rules.
Runner version and platform
GitHub-hosted runner 2.337.0, Ubuntu, Node.js v22.23.2. Completed run.
Source
In ExpressionUtility.ParseNumber, the hex and octal branches explicitly require lowercase x and o; there is no binary branch. After other conversions fail, the function returns Double.NaN. This explains the false comparisons rather than a workflow syntax error.
Job log output
{"input":"0x1F","comparedTo":"31","actionsEqual":"true","javascriptEqual":true}
{"input":"0X1F","comparedTo":"31","actionsEqual":"false","javascriptEqual":true}
{"input":"0o17","comparedTo":"15","actionsEqual":"true","javascriptEqual":true}
{"input":"0O17","comparedTo":"15","actionsEqual":"false","javascriptEqual":true}
{"input":"0b101","comparedTo":"5","actionsEqual":"false","javascriptEqual":true}
{"input":"1e309","comparedTo":"Infinity","actionsEqual":"true","javascriptEqual":true}
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/Sdk/DTExpressions2/Expressions2/Sdk/ExpressionUtility.cs at ExpressionUtility.ParseNumber, then review the linked workflow probe and its six comparison results. Done means uppercase hexadecimal and octal prefixes and binary strings have behavior consistent with the chosen resolution: matching Number(), or having the supported differences documented clearly.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, github-actions
- Domain
- ci-cd, devops
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 72/100