Double Division

  • Thread starter Thread starter shapper
  • Start date Start date
S

shapper

Hello,

To create a double division do I need to define all parts and result
as double?

For example:
Double ratio = (Double)((Double)920 / (Double)image.Width);

Thanks,
Miguel
 
shapper said:
Hello,

To create a double division do I need to define all parts and result
as double?

For example:
Double ratio = (Double)((Double)920 / (Double)image.Width);

Thanks,
Miguel

I'm not sure why you didn't just try it. But, at any rate, the results of
most mathmatical expressions that have any kind of decimal in them are
treated as double by the compiler, so most of the time, you just need to
ensure that your result variable is of the double type or your result is
casted to the type of your result variable.

-Scott
 
Scott said:
[...] you just need to
ensure that your result variable is of the double type or your result is
casted to the type of your result variable.

The type of the expression does not depend on the type of the target
(i.e. the "result variable"), nor does casting the _result_ affect the
outcome.

In other words:

int i = 920, j = 800;
double d = i / j;

That will result in "d" containing "1.0" as the value.

int i = 920, j = 800;
double d = (double)(i / j);

Likewise.

On the other hand, one can write:

int i = 920, j = 800;
double d = (double)i / j;

Promoting just one of the operands to "double" is sufficient to cause
the compiler to promote the other operand to "double" as well for the
purposes of the calculation. In that case, the result is the value
"1.15" (to the nearest hundredths, that is).

Pete
 
nor does casting the _result_ affect the outcome.

I think you read my statement incorrectly. I did not mean that if you
didn't cast the result *variable* then the result would be casted to the
type of the result variable, I meant that either you can ensure your result
variable is of the double type (as you showed by casting one of the
operands) or (as a different approach when you are not interested in getting
a double) ensure that your result is casted to the type of your result
variable as in:

double x = 7, y = 3;

int result = (int)(x / y);



-Scott
 
Scott said:
I think you read my statement incorrectly. I did not mean that if you
didn't cast the result *variable* then the result would be casted to the
type of the result variable,

Since I don't even know what that sentence is supposed to mean, I can
assure you that's not how I read your post to mean. It may yet be that
I've read your post incorrectly, but I definitely didn't read it the way
you seem to think I did.

Besides, casting the "result *variable*" doesn't seem relevant to me at
all, given that it only makes sense to cast a variable when reading from
it and in all the discussion here, no one has mentioned using (i.e.
reading) the result variable after it's been assigned to.
I meant that either you can ensure your result
variable is of the double type (as you showed by casting one of the
operands)

Casting an operand doesn't change the type of the result variable (i.e.
the variable into which the result of the expression is stored), so I
have no idea what you mean by "you can ensure your result variable is of
the double type". The only way to "ensure your result variable is of
the double type" is to use the type "double" in the declaration of the
result variable.

If you mean to refer to something other than the variable into which the
result is stored, you may want to consider using a word other than
"variable". The word "variable" refers specifically to a named
identifier in the C# program into which you can store a value.
or (as a different approach when you are not interested in getting
a double) ensure that your result is casted to the type of your result
variable as in:

double x = 7, y = 3;

int result = (int)(x / y);

Again, the above doesn't affect the data type used in the calculation.
It only affects the value stored in the result variable. In particular:

double x = 7.9, y = 2.9;

int result1 = (int)(x / y),
result2 = (int)x / (int)y;

....produces two different results.

The original question is asking about the division itself, not how to
store the result. And with respect to the division itself, it's the
type of the operands that matter, not what happens to the result of the
division afterwards.

Any discussion about casting the expression itself, or the type of the
variable into which the result of the expression is stored, is IMHO
irrelevant to the original question the OP has asked and can only serve
to distract from the answer to that question.

Pete
 
You have completely misunderstood what I've said. None of what you wrote is
applicable.

No need to drag this out.
 
shapper said:
To create a double division do I need to define all parts and result
as double?

For example:
Double ratio = (Double)((Double)920 / (Double)image.Width);

The rule is that the types in any part of an expression are promoted to the
widest of the participants in that part. For example:

Single result = 3 * 4 / 5.0 + 6;

In "3*4", both are integers, so both are kept as integers. Next, we divide
that result by a double. The integer (12) will be promoted to a double,
and the result of the division is a double. Next, we add that double (2.6)
to an integer (6). The integer will be promoted to a double, and a
floating point addition is done.

Finally, that double result is truncated back to a single for the
assignment.
 
Tim said:
The rule is that the types in any part of an expression are promoted to the
widest of the participants in that part.

Sort of. That is, that's not actually the rule, but if you qualify your
statement as "the rule is, as long as no compile-time error occurs,
generally speaking the types are promoted to the widest..." then it's
reasonably close enough to have predictive value with respect to what
the eventual type of the expression will be (*).

(*) (but even there, there's an exception...see below for what happens
with "uint" when mixed with signed data types)

The _actual_ rule is that numeric promotion is done during operator
overload resolution, and types are promoted only as necessary and
possible in order to allow the operands to be used in a particular
overload of an operator.

The C# specification actually enumerates the rules in the context of
binary operators as follows (see "7.2.6.2 Binary numeric promotions"):

Binary numeric promotion consists of applying
the following rules, in the order they appear here:

• If either operand is of type decimal, the
other operand is converted to type decimal,
or a compile-time error occurs if the other
operand is of type float or double.

• Otherwise, if either operand is of type
double, the other operand is converted to
type double.

• Otherwise, if either operand is of type
float, the other operand is converted to
type float.

• Otherwise, if either operand is of type
ulong, the other operand is converted to
type ulong, or a compile time error occurs
if the other operand is of type sbyte, short,
int, or long.

• Otherwise, if either operand is of type
long, the other operand is converted to
type long.

• Otherwise, if either operand is of type
uint and the other operand is of type sbyte,
short, or int, both operands are converted
to type long.

• Otherwise, if either operand is of type
uint, the other operand is converted to type
uint.

• Otherwise, both operands are converted to
type int.

But note that these are simply the outcome of the available implicit
conversion rules, as applied in the context of operator overloading.
I.e. matching the operands to the available overloads for the operator
in question, and selecting the one that matches best according to the
implicit conversions that exist.

Of particular note: "decimal" is technically "wider than" "double" or
"float", but there is no implicit conversion from decimal to either
double or float. Mixing decimal with double or float doesn't result in
the expression being of type decimal; instead, you just get a compile
time error.

Note also that since there's no built-in type that can allow promotion
of any signed types when used with "ulong", because the compiler would
have to promote both the "ulong" and the signed type to something even
wider than the "ulong", and there is no such type in C#. (On the other
hand, if one of the operands is "uint", the compiler _can_ promote
everything to "long" and preserve the values, so "uint" does work fine
with signed types).

As you can see, it's not sufficient to look simply at the width of the
type. The promotion may actually be to something wider than _any_ of
the operands involved. And other factors may come into play that
prevent numeric promotion, depending on the exact relationship between
the types of the operands.

However, in cases where those factors do come into play, the end result
is simply a failure to compile the expression. So, at the very least
you can be assured that if it does compile, the compiler has selected
some kind of implicit conversion (which does generally involve widening
at least one of the operands, if not both) that is guaranteed to
preserve the values in question, and which matches some available
overload of the operator being used.

In that sense, with those caveats, yes...one can reinterpret the rule to
be based on the ability to widen one or both of the operands numeric
data type.

But it's important to keep in mind that that's not precisely the rule.
The rule is more explicit than that, and is dependent not specifically
on widening data types, but rather the interaction between the implicit
conversion rules and the overload resolution rules.

Note also that this means there are consistent results when dealing with
user-defined operators and implicit conversions. They will follow the
same rules as the built-in conversions and operators, and of course
user-defined conversions may or may not be tied to whether a data type
is widened or not. Depending on the conversion, it's entirely possible
that the idea of "widening" doesn't even apply, but the operator
overload resolution will still happen in a predictable, well-defined way.
For example:

Single result = 3 * 4 / 5.0 + 6;

[...]
Finally, that double result is truncated back to a single for the
assignment.

No. There's no implicit conversion from double to Single (i.e.
"float"). If you don't cast the expression, a compile time error will
occur.

Pete
 
rossum said:
That works when casting from double to int. It fails when casting the
other way from int to double:

int x = 7, y = 3;

double d1 = (double)(x / y); // d1 = 2.0

double d2 = ((double)x / y); // d2 = 2.33333333

double d3 = x / (1.0 * y); // d3 = 2.33333333

Look at the values of d1, d2 and d3.


rossum

And? d1, d2, and d3 wind up with double values, just as they should. What
failure are you describing? All code compiles and runs as expected. In
fact, in this case, no explicit casting is even necessary because (as the
original post is all about) the compiler likes to evaluate most expressions
to a double in the first place. Essentially, the compiler will do implicit
conversions when there is no chance of data loss (int to double), which is
an example of a widening conversion, but for narrowing converstions (when
there is a chance of data loss), you will have to perform an explicit cast.


-Scott
 
Back
Top