What is the difference between "decimal. Divide" and the traditional "/" in C#?

Asked

Viewed 821 times

10

decimal a = 10/5;

Returns 2

decimal b = decimal.Divide(10,5);

Returns 2

3 answers

10


None practical, it’s just a different syntactic way, they perform exactly the same thing. At least if we are only talking about this specific example or use with decimals.

See the operator source. Documentation. No. NET Core has changed a little but it is still the same as the operator on this platform.

    public static Decimal operator /(Decimal d1, Decimal d2) {
        FCallDivide (ref d1, ref d2);
        return d1;
    }

IL code generated using the operator:

.maxstack  4
.locals init (valuetype [mscorlib]System.Decimal V_0)
IL_0000:  nop
IL_0001:  ldsfld     int32 Program::x
IL_0006:  ldsfld     int32 Program::y
IL_000b:  div
IL_000c:  call       valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int32)
IL_0011:  stloc.0
IL_0012:  ldstr      "{0} / {1} = {2}"
IL_0017:  ldsfld     int32 Program::x
IL_001c:  box        [mscorlib]System.Int32
IL_0021:  ldsfld     int32 Program::y
IL_0026:  box        [mscorlib]System.Int32
IL_002b:  ldloc.0
IL_002c:  box        [mscorlib]System.Decimal
IL_0031:  call       string [mscorlib]System.String::Format(string,
                                                            object,
                                                            object,
                                                            object)
IL_0036:  call       void [mscorlib]System.Console::WriteLine(string)
IL_003b:  nop
IL_003c:  call       void Program::Teste()
IL_0041:  nop
IL_0042:  ret

Now let’s use decimal values as operands:

.maxstack  4
.locals init (valuetype [mscorlib]System.Decimal V_0)
IL_0000:  nop
IL_0001:  ldsfld     valuetype [mscorlib]System.Decimal Program::x
IL_0006:  ldsfld     valuetype [mscorlib]System.Decimal Program::y
IL_000b:  call       valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Division(valuetype [mscorlib]System.Decimal,
                                                                                              valuetype [mscorlib]System.Decimal)
IL_0010:  stloc.0
IL_0011:  ldstr      "{0} / {1} = {2}"
IL_0016:  ldsfld     valuetype [mscorlib]System.Decimal Program::x
IL_001b:  box        [mscorlib]System.Decimal
IL_0020:  ldsfld     valuetype [mscorlib]System.Decimal Program::y
IL_0025:  box        [mscorlib]System.Decimal
IL_002a:  ldloc.0
IL_002b:  box        [mscorlib]System.Decimal
IL_0030:  call       string [mscorlib]System.String::Format(string,
                                                            object,
                                                            object,
                                                            object)
IL_0035:  call       void [mscorlib]System.Console::WriteLine(string)
IL_003a:  nop
IL_003b:  call       void Program::Teste()
IL_0040:  nop
IL_0041:  ret

See the source of the method. Documentation

    public static Decimal Divide(Decimal d1, Decimal d2)
    {
        FCallDivide (ref d1, ref d2);
        return d1;
    }

IL code generated:

.maxstack  4
.locals init (valuetype [mscorlib]System.Decimal V_0)
IL_0000:  nop
IL_0001:  ldsfld     int32 Program::x
IL_0006:  call       valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int32)
IL_000b:  ldsfld     int32 Program::y
IL_0010:  call       valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int32)
IL_0015:  call       valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::Divide(valuetype [mscorlib]System.Decimal,
                                                                                         valuetype [mscorlib]System.Decimal)
IL_001a:  stloc.0
IL_001b:  ldstr      "{0} Divide {1} = {2}"
IL_0020:  ldsfld     int32 Program::x
IL_0025:  box        [mscorlib]System.Int32
IL_002a:  ldsfld     int32 Program::y
IL_002f:  box        [mscorlib]System.Int32
IL_0034:  ldloc.0
IL_0035:  box        [mscorlib]System.Decimal
IL_003a:  call       string [mscorlib]System.String::Format(string,
                                                            object,
                                                            object,
                                                            object)
IL_003f:  call       void [mscorlib]System.Console::WriteLine(string)
IL_0044:  nop
IL_0045:  ret

Now let’s use decimal values as operands:

.maxstack  4
.locals init (valuetype [mscorlib]System.Decimal V_0)
IL_0000:  nop
IL_0001:  ldsfld     valuetype [mscorlib]System.Decimal Program::x
IL_0006:  ldsfld     valuetype [mscorlib]System.Decimal Program::y
IL_000b:  call       valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::Divide(valuetype [mscorlib]System.Decimal,
                                                                                         valuetype [mscorlib]System.Decimal)
IL_0010:  stloc.0
IL_0011:  ldstr      "{0} Divide {1} = {2}"
IL_0016:  ldsfld     valuetype [mscorlib]System.Decimal Program::x
IL_001b:  box        [mscorlib]System.Decimal
IL_0020:  ldsfld     valuetype [mscorlib]System.Decimal Program::y
IL_0025:  box        [mscorlib]System.Decimal
IL_002a:  ldloc.0
IL_002b:  box        [mscorlib]System.Decimal
IL_0030:  call       string [mscorlib]System.String::Format(string,
                                                            object,
                                                            object,
                                                            object)
IL_0035:  call       void [mscorlib]System.Console::WriteLine(string)
IL_003a:  nop
IL_003b:  ret

Note that both are static methods (even if one is syntactically an operator). Both accept overload, but these do not. Neither override. Both have the same parameters, and the same return. Both use the same upcast implicit in the parameters/operands, after all the cast is an independent operation. Then promotion of other types occurs in both. They generate the same exceptions.

You can notice that it ends up being the same. The compiler optimizes both for the same code.

Obviously I used variables that the compiler can’t optimize to simulate real use. With literals can generate different code by some compiler deficiency that I wouldn’t know how to explain. The use of variables is the common case. Testing without context can be tricky. In the effort to avoid contaminating the test object with other effects, we often compare things that really wouldn’t happen in real code usage.

Behold the code used in ideone. And in the .NET Fiddle. Also put on the Github for future reference.

There are differences

There is difference in the generated code between the operator and the method. There is also difference when the arguments are decimal and not integer.

It is noticed that with integer the division is done natively (will use processor instructions for integers) and then there will be a type promotion with the result. With the split the promotion occurs already with the method arguments.

With the use of decimal arguments there is no promotion and it is noteworthy that two methods are called, one making the times of the operator (op_Division()) and the other is the obvious.

Precisely because of this difference, the results may be different when the division is not exact when using integer values. The same doesn’t happen when the values are decimal. One of the reasons to use the method is to ensure that the promotion occurs. Of course this is possible with the operator. Here it has to be explicit:

decimal a = (decimal)10 / (decimal)5;

Then the IL is the same as the division made only with decimals.

Completion

If the doubt is only about the difference between using the operator or the method, which was my first interpretation, the difference is purely syntactic.

If the intention of the question is to consider what happens if the operands are integers, then there is a fundamental difference in the result (found value and performance that may not be so negligible), since it is a totally different operation that will be executed.

If the intention was to compare these exact codes, the first is doing a division of integers and promoting the result to decimal and the second promotes integers to decimals and makes a division of decimals.

  • 1

    For those who think in negative, I tried to base the best that gave. My answer is opposite to the other existing. Obviously one of the two is wrong. I trust mine, but if someone can show some error, I would like to learn the right.

7

The answers of Maniero and Gypsy Morrison Mendez are correct, pointing out the differences in methods for operators. But in this particular example, there is no difference (*) - the variables a and b will have the value 2 (decimal). How the value was calculated is that it is a little different.

In the first case (decimal a = 10/5;), the values whole 10 and 5 are divided, resulting in the value whole 2. This value is then converted to the decimal type and assigned to the variable a.

In the second case (decimal b = decimal.Divide(10,5);), integer values 10 and 5 are converted to type decimal, and then the method decimal.Divide is called, with the return value 2 (decimal) is assigned to the variable b (**).

That is, the two methods took different paths to achieve the same result. If you had two integers whose division was not exact (e. g., decimal c = 10 / 3;), the result would be different.

(*) Okay, technically there’s a difference, but it doesn’t affect the outcome; if there’s any difference in performance it’s negligible.

(**) In fact, if you have using the constants 10 and 5 in the first case, the compiler will probably be smart enough to perform that operation directly, and that operation will be equivalent to decimal a = 2;.

  • Good answer. I improved my based on what you put.

4

/ is an operator. Decimal.Divide a method.

  • An operator accepts overload; a method accepts polymorphism. The concepts are different;
  • An operator has several types of return; a method has some depending on the signature of the method used. In the case of your operation, the numbers are not exactly decimal. They may be integers, but the compiler infers that they are decimal by their declaration;
  • Though the return be the same, Decimal.Divide is a much more restrictive way (and therefore more suited to strong typing) than the division operator, who needs to intuit the type of return according to the operators.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.