Function of type unsigned int returns negative number
Asked Answered
M

4

2

Wow I thought I knew my C++ but this is weird

This function returns an unsigned int so I thought that means I will never get a negative number returned right?

The function determines how many hours ahead or behind of UTC you are. So for me I'm in Australia, Sydney so I am +10 GMT which means I am UTC = LocalTime + (-10). Therefore the GetTimeZoneInformation correctly determines I am -10.

BUT my function returns an unsigned int so shouldn't it return 10 not -10?

unsigned int getTimeZoneBias()
{
    TIME_ZONE_INFORMATION tzInfo;
    DWORD res  = GetTimeZoneInformation( &tzInfo );

    if ( res == TIME_ZONE_ID_INVALID )
    {
        return (INT_MAX/2); 
    }

    return (unsigned int(tzInfo.Bias / 60));  // convert from minutes to hours         
}

TCHAR ch[200];
_stprintf( ch, _T("A: %d\n"), getTimeZoneBias()); // this prints out A: -10
debugLog += _T("Bias: ") + tstring(ch) + _T("\r\n");
Markham answered 1/10, 2011 at 4:10 Comment(1)
Part of the problem is that printing unsigned requires %u, in that case it is a duplicate of this question, but there are a lot more problems here with casting back and forth int & unsigned.Onerous
N
4

Here's what I think is happening:

The value of tzInfo.Bias is actually -10. (0xFFFFFFF6) On most systems, casting a signed integer to an unsigned integer of the same size does nothing to the representation.

So the function still returns 0xFFFFFFF6.

But when you print it out, you're printing it back as a signed integer. So it prints-10. If you printed it as an unsigned integer, you'll probably get 4294967286.

What you're probably trying to do is to get the absolute value of the time difference. So you want to convert this -10 into a 10. In which you should return abs(tzInfo.Bias / 60).

Nobile answered 1/10, 2011 at 4:13 Comment(1)
Thanks I was getting that 4294967286 which was confusing me. I really need to rethink when I write simple functions!Markham
F
10

You are trying to print an unsigned int as a signed int. Change %d to %u

_stprintf( ch, _T("A: %u\n"), getTimeZoneBias());
                       ^

The problem is that integers aren't positive or negative by themselves for most computers. It's in the way they are interpreted.

So a large integer might be indistinguishable from a small (absolute value) negative one.

Friend answered 1/10, 2011 at 4:11 Comment(2)
Say nxxxx where x is a bit 0 or 1 and n is either - or +. If you represent your signed vs unsigned numbers, then you can have 10000 for negative zero and 00000 for positive zero and 10000 != 00000. When you do addition, 01111 + 00001 = 10000. You get weird carries. One's compliment is like this, but the numbers are inversed. 5 is 0101 and negative 5 is 1010. You still get the weird 0 vs -0 problem, which in some cases acts as an inverser or -1 or +1. Two's compliment is One's compliment, but +1. So 5 is 0101 and negative 5 is 1010+1 = 1011. This gets rid of the weird 0 and -0 problem magicallyConveyor
If you want to learn more from my other comment, see Binary: Plusses & Minuses (Why We Use Two's Complement) - Computerphile by Computerphile on youtube. youtube.com/watch?v=lKTsv6iVxV4Conveyor
N
4

Here's what I think is happening:

The value of tzInfo.Bias is actually -10. (0xFFFFFFF6) On most systems, casting a signed integer to an unsigned integer of the same size does nothing to the representation.

So the function still returns 0xFFFFFFF6.

But when you print it out, you're printing it back as a signed integer. So it prints-10. If you printed it as an unsigned integer, you'll probably get 4294967286.

What you're probably trying to do is to get the absolute value of the time difference. So you want to convert this -10 into a 10. In which you should return abs(tzInfo.Bias / 60).

Nobile answered 1/10, 2011 at 4:13 Comment(1)
Thanks I was getting that 4294967286 which was confusing me. I really need to rethink when I write simple functions!Markham
A
2

One error is in your _T call. It should be:

_T("A: %u\n")

The function does return a non-negative integer. However, by using the wrong printf specifier, you're causing it to get popped off the stack as an integer. In other words, the bits are interpreted wrong. I believe this is also undefined behavior.

Americanize answered 1/10, 2011 at 4:12 Comment(7)
Yeah but wouldn't the function getTimeZoneBias() ALWAYS return a positive number anyway so I shouldn't have to worry about the formatting coz its always going to be a positive number??Markham
@Matthew Flaschen It's not undefined behavior, it's implementation-defined.Friend
It is a positive number, you are simply misinterpreting it as a negative number. You are commanding the compiler to misinterpret it by telling it that it's a signed number when it's an unsigned number.Aime
@user593747: No. The "%u" format expects an argument of type unsigned int; "%d" expects an argument of type int. An unsigned int object is always non-negative (not necessarily positive; it could be 0), but if you pretend that it's an int, it can easily appear to be negative. For example, if int is 32 bits, then the unsigned int value 4294967295 has the same representation as the int value -1.Straggle
@cnicutar: Do you have a reference for your assertion?Hobbism
@Charles Bailey For C99 I think it is settled by 6.3.1.3/3. I have nothing to say about C++Friend
@cnicutar: I don't believe that 6.3.1.3/3 is the whole story, what about @JerryCoffin's argument: #5852024. As (unsigned int)-10 is too large to be represented as an int on a two's complement architecture I would have thought that the behaviour was undefined.Hobbism
B
1

As other people have pointed out, when you cast to an unsigned int, you are actually telling the compiler to use the pattern of bits in the int and use it as an unsigned int. If your computer uses two's complement, as most do, then your number will be interpreted as UINT_MAX-10 instead of 10 as you expected. When you use the %d format specifier, the compiler goes back to using the same bit pattern as an int instead of an unsigned int. This is why you are still getting -10.

If you want the absolute value of an integer, you should try to get it mathematically instead of using a cast.

Bradney answered 1/10, 2011 at 4:21 Comment(3)
is it unsafe to make these assumptions (rely on computer being 2's complement?) when writing code?Samson
I'm not sure what you mean. Most commonly used languages use 2's complement, but it almost never matters when writing code. It mostly determines edge cases and how bit operations affect numbers. If you're not sure and you need to know, you should always check.Bradney
something along the lines of "your C-code should always be hardware-agnostic". Or perhaps the adage is "compiler-agnostic". I will go brush up on 2's complement alternatives and ramifications in compilers. Thanks!Samson

© 2022 - 2024 — McMap. All rights reserved.