Weird behavior of -1U and -1UL [duplicate] - c#

This question already has answers here:
Unsigned Integer Literal Having Negative Sign
(3 answers)
Closed 2 years ago.
I'm currently creating a unit test for a method.
The convert method this is supposed to test basically consists of return if value > 0 so I had the idea to check unsigned overflows as well.
While creating test cases for the test I stumbled upon this very peculiar behavior of the U and UL suffixes.
[DataTestMethod]
// more data rows
[DataRow(-1U, true)] // Technically compiles, but not to what I expected or "wanted"
[DataRow(-1UL, true)] // "CS0023: Operator '-' cannot be applied to operand of type 'ulong'"
// more data rows
public void TestConvertToBool(object value, bool result)
{
// For testing purposes
ulong uLong = -1UL; // "CS0023: Operator '-' cannot be applied to operand of type 'ulong'"
uint uInt = -1U; // "CS0266: Cannot implicitly convert type 'long' to 'uint'. An explicit conversion exists (are you missing a cast?) - Cannot convert source type 'long' to target type 'uint'"
var foo = -1U; // foo is of type 'long'
var bar = 1U; // bar is of type 'uint'
// ... do the actual assertion here
}
Why do they behave this way? Shouldn't it just overflow to the max values of uint and ulong?
I couldn't find any answer to this. The reference source seems to only contain the wrapper objects UInt32 and such that don't include any operators.
Note: I'm aware that there is no real point in doing this in the first place. I just found the behavior to be very unexpected.

It seems that it's converting the uint to a long so that there are always enough bits to store the entire potential range of resulting positive and negative values. The - operator can't be applied to a ulong because there's no larger type to convert it to

Related

Why can't I concatenate int data in C#? [duplicate]

This question already has answers here:
Cannot implicitly convert type 'double' to 'int'. -error
(4 answers)
Closed 9 months ago.
I am a beginner in C#. I'm currently trying to concatenate int data and using the Math square root function, but after running the program, it produces an error.
The code are as follows.
using System;
namespace NewProject
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Type any number");
int number = Convert.ToInt32(Console.ReadLine());
int square_root = Math.Sqrt(number);
Console.WriteLine("$The square root of {square_root} is " , square_root);
}
}
}
C:\Users\ajmal\Documents\Learn C#\New Project\Program.cs(13,31): error CS0266: Cannot implicitly convert type 'double' to 'int'. An explicit conversion exists (are you missing a cast?) [C:\Users\ajmal\Documents\Learn C#\New Project\N
ew Project.csproj]
The build failed. Fix the build errors and run again.
Any tips and help would be appreciated
The compiler message tells:
Cannot implicitly convert type 'double' to 'int'. An explicit conversion exists (are you missing a cast?)
It even gives you the location of the error:
New Project\Program.cs(13,31)
Line 13, position 31
If you look there, not very far you should find this line:
int square_root = Math.Sqrt(number);
If you look that the Math.Sqrt documentation, you'll see that Math.Sqrt returns a double.
Here is the signature from the documentation:
public static double Sqrt (double d);
So you try to put a double into an int (the type of your square_root variable), which is only possible with an explicit cast, hence the compiler message.
To fix this (supposing you want to keep a non integer value for the square root) simply correct the line with
double square_root = Math.Sqrt(number);
or you can even let the compiler do the work by leveraging the var keyword:
var square_root = Math.Sqrt(number);
Additional remarks:
The Math.Sqrt signature shows that a double is needed as parameter, but you pass an int. This is not a problem since the compiler can do an implicit conversion from an int to a double.
There will also be an error on the following line (without even considering the meaning of what you write).
Console.WriteLine("$The square root of {square_root} is " , square_root);
To concatenate strings, you can use several techniques, like composite formating and string interpolation. It seems you mix both synthax on this line.
See the string interpolation documentation:
an example of both methods taken from the documentaion:
string name = "Mark";
var date = DateTime.Now;
// Composite formatting:
Console.WriteLine("Hello, {0}! Today is {1}, it's {2:HH:mm} now.", name, date.DayOfWeek, date);
// String interpolation:
Console.WriteLine($"Hello, {name}! Today is {date.DayOfWeek}, it's {date:HH:mm} now.");
// Both calls produce the same output that is similar to:
// Hello, Mark! Today is Wednesday, it's 19:40 now.

why adding 1 to byte.MaxValue results in 256 not an overflow [duplicate]

This question already has answers here:
byte + byte = int... why?
(16 answers)
Closed 3 years ago.
I have this simple piece of code in C# to show overflow error when adding 1 to MaxValue of types.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
byte a = byte.MaxValue;
byte b = 1;
Console.WriteLine("Max+1 is : {0}", a+b);
Console.ReadLine();
}
}
}
But instead of overflow and a wrong result, it generates correct value: 256
Why?
While maximum value of byte variable in C# is 255.
Am I wrong?
Addition on anything less than a int is done in int. This is described in the Standard ECMA-334 C# specification under Numeric promotions
12.4.7 Numeric promotions
When overload resolution rules (ยง12.6.4) are applied to this set of
operators, the effect is to select the first of the operators for
which implicit conversions exist from the operand types. [Example: For
the operation b * s, where b is a byte and s is a short, overload
resolution selects operator *(int, int) as the best operator. Thus,
the effect is that b and s are converted to int, and the type of the
result is int. Likewise, for the operation i * d, where i is an int
and d is a double, overload resolution selects operator *(double,
double) as the best operator. end example]
Additionally, if you want to catch overflows you will need to use the checked keyword
Example
byte a = byte.MaxValue;
byte b = 1;
byte c = checked((byte)(a + b));
Additional Resources
checked (C# Reference)
By default, an expression that contains only constant values causes a
compiler error if the expression produces a value that is outside the
range of the destination type. If the expression contains one or more
non-constant values, the compiler does not detect the overflow.
Evaluating the expression assigned to i2 in the following example does
not cause a compiler error.

Casting a hex number under unchecked scope [duplicate]

I want to use HEX number to assign a value to an int:
int i = 0xFFFFFFFF; // effectively, set i to -1
Understandably, compiler complains.
Question, how do I make above work?
Here is why I need this.
WritableBitmap class exposes pixels array as int[]. So if I want to set pixel to Blue, I would say: 0xFF0000FF (ARGB) (-16776961)
Plus I am curious if there is an elegant, compile time solution.
I know there is a:
int i = BitConverter.ToInt32(new byte[] { 0xFF, 0x00, 0x00, 0xFF }, 0);
but it is neither elegant, nor compile time.
Give someone a fish and you feed them for a day. Teach them to pay attention to compiler error messages and they don't have to ask questions on the internet that are answered by the error message.
int i = 0xFFFFFFFF;
produces:
Cannot implicitly convert type 'uint' to 'int'. An explicit conversion exists
(are you missing a cast?)
Pay attention to the error message and try adding a cast:
int i = (int)0xFFFFFFFF;
Now the error is:
Constant value '4294967295' cannot be converted to a 'int'
(use 'unchecked' syntax to override)
Again, pay attention to the error message. Use the unchecked syntax.
int i = unchecked((int)0xFFFFFFFF);
Or
unchecked
{
int i = (int)0xFFFFFFFF;
}
And now, no error.
As an alternative to using the unchecked syntax, you could specify /checked- on the compiler switches, if you like to live dangerously.
Bonus question:
What makes the literal a uint in the first place?
The type of an integer literal does not depend on whether it is hex or decimal. Rather:
If a decimal literal has the U or L suffixes then it is uint, long or ulong, depending on what combination of suffixes you choose.
If it does not have a suffix then we take the value of the literal and see if it fits into the range of an int, uint, long or ulong. Whichever one matches first on that list is the type of the expression.
In this case the hex literal has a value that is outside the range of int but inside the range of uint, so it is treated as a uint.
You just need an unchecked cast:
unchecked
{
int i = (int)0xFFFFFFFF;
Console.WriteLine("here it is: {0}", i);
}
The unchecked syntax seems a bit gar'ish (!) when compared to the various single-letter numerical suffixes available.
So I tried for a shellfish:
static public class IntExtMethods
{
public static int ui(this uint a)
{
return unchecked((int)a);
}
}
Then
int i = 0xFFFFFFFF.ui();
Because the lake has more fish.
Note: it's not a constant expression, so it can't be used to initialize an enum field for instance.

Do I need to cast enums to their numerical counterpart? [duplicate]

This question already has answers here:
Byte enum comparison in C#
(3 answers)
Closed 8 years ago.
I have set up a enum like this:
public enum ServerCommands:byte {
New = 0,
Join = 1,
}
And I want to use it like this:
byte command = buffer[0];
if (command == ServerCommands.Join) // Error: Operator == cannot be operands of type 'byte' and 'ServerCommands'.
Why is this not possible to do and how can I make it work? They are both of type byte.
You still need to cast from byte to ServerCommands! This is not done automatically. Assinging numbers to enum values is just for clarity when casting enum to int or other permitted numeric types.
Assigning numeric values to enum values does not change their type to the numeric type! You can cast any enum value to an int, as all enums (if not declared otherwise) can be cast to int, the first enum value having int value 0.
public enum MyEnum
{
First,
Second
}
is equal to
public enum MyEnum : int
{
First = 0,
Second
}
The feature of numbering enum values is required if the number is not linear, as in:
public enum ErrorCodes: int
{
Success = 0,
FileNotFound = 1,
MissingRights = 5,
WhatTheHeck = 18
}
You better state conversions explicitly:
if (command == (byte) ServerCommands.Join)
or even better:
if ((ServerCommands) command == ServerCommands.Join) //always convert to the more restrictive type.
This is a precaution to prevent one from comparing values without knowing that the objects are from a different type.
Equality means that both objects have the same type. This is not the case. ServerCommands extend from byte. Thus a byte is not per se a valid ServerCommands object...
Furthermore the : byte is more used as an explicit encoding. Having the same binary encoding does not imply two objects are the same. For instance 14.25f and 0x41640000 are binary the same...
You probably need to cast your enum:
if (command == (byte)ServerCommands.Join)

Why does this implicit conversion from int to uint work?

Using Casting null doesn't compile as inspiration, and from Eric Lippert's comment:
That demonstrates an interesting case. "uint x = (int)0;" would
succeed even though int is not implicitly convertible to uint.
We know this doesn't work, because object can't be assigned to string:
string x = (object)null;
But this does, although intuitively it shouldn't:
uint x = (int)0;
Why does the compiler allow this case, when int isn't implicitly convertible to uint?
Integer constant conversions are treated as very special by the C# language; here's section 6.1.9 of the specification:
A constant expression of type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant-expression is within the range of the destination type. A constant expression of type long can be converted to type ulong, provided the value of the constant expression is not negative.
This permits you to do things like:
byte x = 64;
which would otherwise require an ugly explicit conversion:
byte x = (byte)64; // gross
The following code wil fail with the message "Cannot implicitly convert type 'int' to 'uint'. An explicit conversion exists (are you missing a cast?)"
int y = 0;
uint x = (int)y;
And this will fail with: "Constant value '-1' cannot be converted to a 'uint'"
uint x = (int)-1;
So the only reason uint x = (int)0; works is because the compiler sees that 0 (or any other value > 0) is a compile time constant that can be converted into a uint
In general compilers have 4 steps in which the code is converted.
Text is tokenized > Tokens are parsed > An AST is built + linking > the AST is converted to the target language.
The evaluation of constants such as numbers and strings occurs as a first step and the compiler probably treats 0 as a valid token and ignores the cast.

Categories

Resources