Hexadecimal notation and signed integers - c#

This is a follow up question. So, Java store's integers in two's-complements and you can do the following:
int ALPHA_MASK = 0xff000000;
In C# this requires the use of an unsigned integer, uint, because it interprets this to be 4278190080 instead of -16777216.
My question, how do declare negative values in hexadecimal notation in c#, and how exactly are integers represented internally? What are the differences to Java here?

C# (rather, .NET) also uses the two's complement, but it supports both signed and unsigned types (which Java doesn't). A bit mask is more naturally an unsigned thing - why should one bit be different than all the other bits?
In this specific case, it is safe to use an unchecked cast:
int ALPHA_MASK = unchecked((int)0xFF000000);
To "directly" represent this number as a signed value, you write
int ALPHA_MASK = -0x1000000; // == -16777216
Hexadecimal is not (or should not) be any different from decimal: to represent a negative number, you need to write a negative sign, followed by the digits representing the absolute value.

Well, you can use an unchecked block and a cast:
unchecked
{
int ALPHA_MASK = (int)0xff000000;
}
or
int ALPHA_MASK = unchecked((int)0xff000000);
Not terribly convenient, though... perhaps just use a literal integer?

And just to add insult to injury, this will work too:
-0x7F000000

Related

Calculators working with larger numbers than 18446744073709551615

When I Initialize a ulong with the value 18446744073709551615 and then add a 1 to It and display to the Console It displays a 0 which is totally expected.
I know this question sounds stupid but I have to ask It. if my Computer has a 64-bit architecture CPU how is my calculator able to work with larger numbers than 18446744073709551615?
I suppose floating-point has a lot to do here.
I would like to know exactly how this happens.
Thank you.
working with larger numbers than 18446744073709551615
"if my Computer has a 64-bit architecture CPU" --> The architecture bit size is largely irrelevant.
Consider how you are able to add 2 decimal digits whose sum is more than 9. There is a carry generated and then used when adding the next most significant decimal place.
The CPU can do the same but with base 18446744073709551616 instead of base 10. It uses a carry bit as well as a sign and overflow bit to perform extended math.
I suppose floating-point has a lot to do here.
This is nothing to do with floating point.
; you say you're using ulong, which means your using unsigned 64-but arithmetic. The largest value you can store is therefore "all ones", for 64 bits - aka UInt64.MaxValue, which as you've discovered: https://learn.microsoft.com/en-us/dotnet/api/system.uint64.maxvalue
If you want to store arbitrarily large numbers: there are APIs for that - for example BigInteger. However, arbitrary size cones at a cost, so it isn't the default, and certainly isn't what you get when you use ulong (or double, or decimal, etc - all the compiler-level numeric types have fixed size).
So: consider using BigInteger
You either way have a 64 bits architecture processor and limited to doing 64 bits math - your problem is a bit hard to explain without taking an explicit example of how this is solved with BigInteger in System.Numerics namespace, available in .NET Framework 4.8 for example. The basis is to 'decompose' the number into an array representation.
mathematical expression 'decompose' here meaning :
"express (a number or function) as a combination of simpler components."
Internally BigInteger uses an internal array (actually multiple internal constructs) and a helper class called BigIntegerBuilder. In can implicitly convert an UInt64 integer without problem, for even bigger numbers you can use the + operator for example.
BigInteger bignum = new BigInteger(18446744073709551615);
bignum += 1;
You can read about the implicit operator here:
https://referencesource.microsoft.com/#System.Numerics/System/Numerics/BigInteger.cs
public static BigInteger operator +(BigInteger left, BigInteger right)
{
left.AssertValid();
right.AssertValid();
if (right.IsZero) return left;
if (left.IsZero) return right;
int sign1 = +1;
int sign2 = +1;
BigIntegerBuilder reg1 = new BigIntegerBuilder(left, ref sign1);
BigIntegerBuilder reg2 = new BigIntegerBuilder(right, ref sign2);
if (sign1 == sign2)
reg1.Add(ref reg2);
else
reg1.Sub(ref sign1, ref reg2);
return reg1.GetInteger(sign1);
}
In the code above from ReferenceSource you can see that we use the BigIntegerBuilder to add the left and right parts, which are also BigInteger constructs.
Interesting, it seems to keep its internal structure into an private array called "_bits", so that is the answer to your question. BigInteger keeps track of an array of 32-bits valued integer array and is therefore able to handle big integers, even beyond 64 bits.
You can drop this code into a console application or Linqpad (which has the .Dump() method I use here) and inspect :
BigInteger bignum = new BigInteger(18446744073709551615);
bignum.GetType().GetField("_bits",
BindingFlags.NonPublic | BindingFlags.Instance).GetValue(bignum).Dump();
A detail about BigInteger is revealed in a comment in the source code of BigInteger on Reference Source. So for integer values, BigInteger stores the value in the _sign field, for other values the field _bits is used.
Obviously, the internal array needs to be able to be converted into a representation in the decimal system (base-10) so humans can read it, the ToString() method converts the BigInteger to a string representation.
For a better in-depth understanding here, consider doing .NET source stepping to step way into the code how you carry out the mathematics here. But for a basic understanding, the BigInteger uses an internal representation of which is composed with 32 bits array which is transformed into a readable format which allows bigger numbers, bigger than even Int64.
// For values int.MinValue < n <= int.MaxValue, the value is stored in sign
// and _bits is null. For all other values, sign is +1 or -1 and the bits are in _bits

Inverting all 32 bits in a number using binary operators in c#

I am wondering how you take a number (for example 9), convert it to a 32 int (00000000000000000000000000001001), then invert or flip every bit (11111111111111111111111111110110) so that the zeroes become ones and the ones become zeroes.
I know how to do that by replacing the numbers in a string, but I need to know how to do that with binary operators on a binary number.
I think you have to use this operator, "~", but it just gives me a negative number when I use it on a value.
That is doing the correct functionality. The int data type within C# uses signed integers, so 11111111111111111111111111110110 is in fact a negative number.
As Marc pointed out, if you want to use unsigned values declare your number as a uint.
If you look at the decimal version of your number then its a negative number.
If you declare it as a unsigned int then its a positive one.
But this doesnt matter, binary it will always be 11111111111111111111111111110110.
Try this:
int number = 9;
Console.WriteLine(Convert.ToString(number, 2)); //Gives you 1001
number = ~number; //Invert all bits
Console.WriteLine(Convert.ToString(number, 2));
//Gives you your wanted result: 11111111111111111111111111110110

C#: Why is 0xFFFFFFFF a uint when it represents -1?

I don't understand why C# considers the literal 0xFFFFFFFF as a uint when it also represents -1 for int types.
The following is code was entered into the Immediate Window shown here with the output:
int i = -1;
-1
string s = i.ToString("x");
"ffffffff"
int j = Convert.ToInt32(s, 16);
-1
int k = 0xFFFFFFFF;
Cannot implicitly convert type 'uint' to 'int'. An explicit conversion exists (are you missing a cast?)
int l = Convert.ToInt32(0xFFFFFFFF);
OverflowException was unhandled: Value was either too large or too small for an Int32.
Why can the string hex number be converted without problems but the literal only be converted using unchecked?
Why is 0xFFFFFFFF a uint when it represents -1?
Because you're not writing the bit pattern when you write
i = 0xFFFFFFFF;
you're writing a number by C#'s rules for integer literals. With C#'s integer literals, to write a negative number we write a - followed by the magnitude of the number (e.g., -1), not the bit pattern for what we want. It's really good that we aren't expected to write the bit pattern, it would make it really awkward to write negative numbers. When I want -3, I don't want to have to write 0xFFFFFFFD. :-) And I really don't want to have to vary the number of leading Fs based on the size of the type (0xFFFFFFFFFFFFFFFD for a long -3).
The rule for choosing the type of the literal is covered by the above link by saying:
If the literal has no suffix, it has the first of these types in which its value can be represented: int, uint, long, ulong.
0xFFFFFFFF doesn't fit in an int, which has a maximum positive value of 0x7FFFFFFF, so the next in the list is uint, which it does fit in.
0xffffffff is 4294967295 is an UInt32 that just happens to have a bit pattern equal to the Int32 -1 due to the way negative numbers are represented on computers. Just because they have the same bit pattern, that doesn't mean 4294967295 = -1. They're completely different numbers so of course you can't just trivially convert between the two. You can force the reintepretation of the bit pattern by using an explicit cast to int: (int)0xffffffff.
The C# docs say that the compiler will try to fit the number you provide in the smallest type that can fit it. That doc is a bit old, but it applies still. It always assumes that the number is positive.
As a fallback you can always coerce the type.
The C# language rules state that 0xFFFFFFFF is an unsigned literal.
A C# signed int is 2's complement type. That scheme uses 0xFFFFFFFF to represent -1. (2's complement is a clever scheme since it doesn't have a signed zero).
For an unsigned int, 0xFFFFFFFF is the largest value it can take, and due to its size, it can't be converted to a signed int.

0x80000000 == 2147483648 in C# but not in VB.NET

In C#:
0x80000000==2147483648 //outputs True
In VB.NET:
&H80000000=2147483648 'outputs False
How is this possible?
This is related to the history behind the languages.
C# always supported unsigned integers. The value you use are too large for int so the compiler picks the next type that can correctly represent the value. Which is uint for both.
VB.NET didn't acquire unsigned integer support until version 8 (.NET 2.0). So traditionally, the compiler was forced to pick Long as the type for the 2147483648 literal. The rule was however different for the hexadecimal literal, it traditionally supported specifying the bit pattern of a negative value (see section 2.4.2 in the language spec). So &H80000000 is a literal of type Integer with the value -2147483648 and 2147483648 is a Long. Thus the mismatch.
If you think VB.NET is a quirky language then I'd invite you to read this post :)
The VB version should be:
&H80000000L=2147483648
Without the 'long' specifier ('L'), VB will try to interpret &H8000000 as an integer. If you force it to consider this as a long type, then you'll get the same result.
&H80000000UI will also work - actually this is the type (UInt32) that C# regards the literal as.
This happens because the type of the hexadecimal number is UInt32 in C# and Int32 in VB.NET.
The binary representation of the hexadecimal number is:
10000000000000000000000000000000
Both UInt32 and Int32 take 32 bits, but because Int32 is signed, the first bit is considered a sign to indicate whether the number is negative or not: 0 for positive, 1 for negative. To convert a negative binary number to decimal, do this:
Invert the bits. You get 01111111111111111111111111111111.
Convert this to decimal. You get 2147483647.
Add 1 to this number. You get 2147483648.
Make this negative. You get -2147483648, which is equal to &H80000000 in VB.NET.

.NET primitives and type hierarchies, why was it designed like this?

I would like to understand why on .NET there are nine integer types: Char, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, and UInt64; plus other numeric types: Single, Double, Decimal; and all these types have no relation at all.
When I first started coding in C# I thought "cool, there's a uint type, I'm going to use that when negative values are not allowed". Then I realized no API used uint but int instead, and that uint is not derived from int, so a conversion was needed.
What are the real world application of these types? Why not have, instead, integer and positiveInteger ? These are types I can understand. A person's age in years is a positiveInteger, and since positiveInteger is a subset of integer there's so need for conversion whenever integer is expected.
The following is a diagram of the type hierarchy in XPath 2.0 and XQuery 1.0. If you look under xs:anyAtomicType you can see the numeric hierarchy decimal > integer > long > int > short > byte. Why wasn't .NET designed like this? Will the new framework "Oslo" be any different?
My guess would be because the underlying hardware breaks that class hierarchy. There are (perhaps surprisingly) many times when you care that a UInt32 is a 4 bytes big and unsigned, so a UInt32 is not a kind of Int32, nor is an Int32 a type of Int64.
And you almost always care about the difference between an int and a float.
Fundamentally, inheritance & the class hierarchy are not the same as mathematical set inclusion. The fact that the values a UInt32 can hold are a strict subset of the values an Int64 can hold does not mean that a UInt32 is a type of Int64. Less obviously, an Int32 is not a type of Int64 - even though there's no conceptual difference between them, their underlying representations are different (4 bytes versus 8 bytes). Decimals are even more different.
XPath is different: the representations for all the numeric types are fundamentally the same - a string of ASCII digits. There, the difference between a short and a long is one of possible range rather than representation - "123" is both a valid representation of a short and a valid representation of a long with the same value.
Decimal is intended for calculations that need precision (basically, money).
See here: http://msdn.microsoft.com/en-us/library/364x0z75(VS.80).aspx
Singles/Doubles are different to decimals, because they're intended to be an approximation (basically, for scientific calculations).
That's why they're not related.
As for bytes and chars, they're totally different: a byte is 0-255, whereas a char is a character, and can hence store unicode characters (there are a lot more than 255 of them!)
Uints and ints don't convert automatically, because they can each store values that are impossible for the other (uints have twice the positive range of ints).
Once you get the hang of it all, it actually does make a lot of sense.
As for your ages thing, i'd simply use an int ;)

Categories

Resources