4

Good afternoon,

Having never used C# to do serious mathematical work, I have just noticed something which left me confused... If it is true that

double Test = Math.Sqrt(UInt64.MaxValue)

is equal to 4294967296.0, that is, UInt32.MaxValue + 1, why is it that

ulong Test2 = UInt32.MaxValue * UInt32.MaxValue;

is equal to 1? At first sight it seems to me that overflow occurs here... But why is that since that product should fit a UInt64?

Thank you very much.

2 Answers 2

10

the first one happens because double doesn't have 64 mantissa bits, but only around 53. So UInt64.MaxValue will be rounded to UInt64.MaxValue+1 during the conversion to double. And the Sqrt of that is obviously 2^32. double can represent any value from (U)Int32 exactly, but some of the larger 64 bit integers can't be represented as double.

The second one happens because you do the multiplication before casting to UInt64, i.e. it happens as UInt32, which obviously overflows. Cast at least one of your operands to UInt64 and the problem will disappear.

Sign up to request clarification or add additional context in comments.

4 Comments

True. Please give -1 to my question. And thank you very much.
@Miguel: I don't think you need to -1. You made an honest mistake. The question's been answered and that's that.
The question was clearly formulated, so I have no complaint. And I made similar mistakes myself too.
@Miguel, keep this in mind in more scenarios than just math. The expression on the right is not affected by the type on the left. The expression on the right has a type of its own (and if it does not, it will produce an error).
4
ulong Test2 = UInt32.MaxValue * UInt32.MaxValue

Could be translated to :

UInt32 __temp = UInt32.MaxValue * UInt32.MaxValue; // Overflow
ulong Test2 = (ulong)__temp;

as thee operation on the left of the = sign is always done without any inference on the type on the right obviously not what you want...

It should have been

ulong Test2 = (long)UInt32.MaxValue * UInt32.MaxValue;

That will be treated as :

ulong Test2 = (long)UInt32.MaxValue * (long)UInt32.MaxValue;

And will work.

The rules are in section 16.4.2 of the C# norm :

Numeric promotion consists of automatically performing certain implicit conversions of the operands of the predefined unary and binary numeric operators. Numeric promotion is not a distinct mechanism, but rather an effect of applying overload resolution to the predefined operators. Numeric promotion specifically does not affect evaluation of user-defined operators, although user-defined operators can be implemented to exhibit similar effects.

As an example of numeric promotion, consider the predefined implementations of the binary * operator:

int operator *(int x, int y); 
uint operator *(uint x, uint y); 
long operator *(long x, long y); 
ulong operator *(ulong x, ulong y); 
void operator *(long x, ulong y); 
void operator *(ulong x, long y); 
float operator *(float x, float y); 
double operator *(double x, double y); 
decimal operator *(decimal x, decimal y); 

When overload resolution rules (§14.4.2) 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]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.