This project is read-only.

The ~/ Operator - Division that always Evaluates to an Integer

Topics: C# Language Design
Sep 26, 2014 at 10:50 PM
Edited Sep 26, 2014 at 11:10 PM
It's division, but it will always evaluate an integer result:
int i0 = 5 / 2;      // Operands are integers, evaluates integer 2.
int i1 = 5.0 / 2.0;  // Operands are doubles, evaluates double 2.5 - Error on assignment
int i2 = 5.0 ~/ 2.0; // Operands are doubles, evaluates integer 2 - No error
It's essentially syntactic sugar for:
int i2 = (int)(5.0 / 2.0);
It could literally just be converted into an (int) cast by the compiler (like above) as opposed to being a 'proper' operator - which should not be too deep of a change.

The assignment operator version would be ~/=
a ~/= b;
I came across this operator in the Dart programming language and thought it would be a very handy addition.

The Dart operator reference can be found here, although there is no explicit explanation of the ~/ operator.
Sep 27, 2014 at 11:28 AM
If this is considered, it would be logical to use the \ operator. This has been used in Visual Basic for many years for integer divide.

Rules deserve attention as C# programmers are often surprised that C# truncates on the implicit integer divide (not round).

7/4 == 1
Sep 28, 2014 at 4:58 PM
Edited Sep 28, 2014 at 4:59 PM
I'm surprised there is a special operator in Dart for this. At least for me, much more common is that you divide two integers and require a double, i.e. (double)5 / 2.
Sep 29, 2014 at 5:45 PM
christoph_hausner wrote:
I'm surprised there is a special operator in Dart for this. At least for me, much more common is that you divide two integers and require a double, i.e. (double)5 / 2.
The various kinds of whole-number and integer division should be considered fundamentally different operations from real-number division; I consider it unfortunate that the creators of FORTRAN decided to notate whole-number division using / rather than .DIV. and other languages like C followed its lead. Although code which divides two floating-point numbers will often want a floating-point result, and code which divides two integers will often want an integer result, neither statement is true 100% of the time. Use of separate operators would have allowed code to ask for whatever kind of division it actually wanted in each particular case.
Sep 29, 2014 at 8:48 PM
This would be unnecessary with C#. The C-heritage of languages differentiates between division operations based on the types of the operands. Dart (and VB) don't differentiate, they always force that / perform a floating point division regardless of the operands and require that the programmer opt-in to integral division through a separate operator. If you want to opt-in to integer division in C# you just need to ensure that both operands are integers.

Dart probably inherited this from JavaScript which only has floating point types.
Sep 29, 2014 at 10:10 PM
Halo_Four wrote:
This would be unnecessary with C#. The C-heritage of languages differentiates between division operations based on the types of the operands.
C# presently has truncating discrete division operators for integer-type operands; it does not have any other form of discrete division operators, though others are often more useful. Having operators for floored and rounded discrete division with either integer or floating-point operands, along with matching remainder operators, could be nice, though for such division operators to be most useful they should allow their result to be implicitly converted to either Int32 or Int64.

If e.g. ~/ were defined as "rounding discrete division" and ~% were defined as the corresponding remainder, that would allow code like n >= 0 ? (n+1)/3 : (n-1)/3 to be replaced with n ~/ 3.
Sep 30, 2014 at 12:52 AM
supercat wrote:
Halo_Four wrote:
This would be unnecessary with C#. The C-heritage of languages differentiates between division operations based on the types of the operands.
C# presently has truncating discrete division operators for integer-type operands; it does not have any other form of discrete division operators, though others are often more useful. Having operators for floored and rounded discrete division with either integer or floating-point operands, along with matching remainder operators, could be nice, though for such division operators to be most useful they should allow their result to be implicitly converted to either Int32 or Int64.

If e.g. ~/ were defined as "rounding discrete division" and ~% were defined as the corresponding remainder, that would allow code like n >= 0 ? (n+1)/3 : (n-1)/3 to be replaced with n ~/ 3.
That is the definition of integer division, and how it is implemented not just in IL but also on all x86 CPUs, if not all CPUs. If you want any other form of rounding you have to cast the values to floating point, divide and then round. With that comes with massive performance penalty of doing so, approximately a full order of magnitude. If that's the behavior you want you're free to use it exactly like VB does it, by casting to System.Double and calling Math.Round.
Dim x As Integer = 10
Dim y As Integer = 3
Dim result As Integer = x / y;
is the same as:
int x = 10;
int y = 3;
int result = (int)(Math.Round((double)x / y, MidpointRounding.ToEven);
Sep 30, 2014 at 5:52 PM
Edited Sep 30, 2014 at 5:54 PM
Although the IDIV instruction performs truncated integer division, it is seldom the most efficient way to divide by a constant. In most cases, floored division by a constant and its associated modulus would be faster than truncated division and the associated remainder. For example, floored division by 4 can be performed via asr ax,2. Halves-round-up division by four could be performed by asr ax,2 / adc ax,0. Either floored division or halves-round-up division by a non-power-of-two constant may be performed via mov ax,const1 / mul src / add ax,const2 / adc dx,const3. In every case I can think of (other than division by one) the instruction sequence is shorter and faster than what would be required for truncating division, even if truncated division didn't necessitate the use of additional user code to adjust the result to match user code requirements.
Sep 30, 2014 at 6:48 PM
supercat wrote:
Although the IDIV instruction performs truncated integer division, it is seldom the most efficient way to divide by a constant.
Of course, and the JIT knows this and will use better sequences of op codes when it has enough information to do so where it is functionality identical to IDIV.

To add new forms of division you'd have to add new corresponding IL op codes (or prefixes), otherwise you'd be stuck with what I mentioned above.
Sep 30, 2014 at 11:23 PM
Halo_Four wrote:
Of course, and the JIT knows this and will use better sequences of op codes when it has enough information to do so where it is functionality identical to IDIV.

To add new forms of division you'd have to add new corresponding IL op codes (or prefixes), otherwise you'd be stuck with what I mentioned above.
When dividing by a constant, I would expect floored or round-half-up division could often be computed more efficiently than truncated division, even if the C# compiler had to generate IL to perform it. For example, suppose you want to define a method to divide by X by a constant SCALE, rounded to the nearest integer, and you don't care which way which way halves get rounded. If SCALE was a known power of two (e.g. eight) and X was known not to be particularly close to the maximum possible integer value, one could simply compute (X+4)>>3. Can you suggest any clean way of writing the code such that it would be no less efficient if SCALE happens to be a power of two, but would yield correct results even if SCALE were some other value?
Oct 1, 2014 at 2:01 AM
supercat wrote:
Halo_Four wrote:
Of course, and the JIT knows this and will use better sequences of op codes when it has enough information to do so where it is functionality identical to IDIV.

To add new forms of division you'd have to add new corresponding IL op codes (or prefixes), otherwise you'd be stuck with what I mentioned above.
When dividing by a constant, I would expect floored or round-half-up division could often be computed more efficiently than truncated division, even if the C# compiler had to generate IL to perform it. For example, suppose you want to define a method to divide by X by a constant SCALE, rounded to the nearest integer, and you don't care which way which way halves get rounded. If SCALE was a known power of two (e.g. eight) and X was known not to be particularly close to the maximum possible integer value, one could simply compute (X+4)>>3. Can you suggest any clean way of writing the code such that it would be no less efficient if SCALE happens to be a power of two, but would yield correct results even if SCALE were some other value?
The efficiency is not as important as the intent. Integer division is expected to behave in one manner so only one IL opcode was dedicated to it. The JIT certainly shouldn't be left to decide that another method might be faster if the outcome would not be the same. And IL shouldn't represent what would be a platform-specific optimization, only the intent. Even in the case of division operations that could be replaced by bit shifting the C# compiler emits a div opcode and allows the JIT to figure out that the divisor is a constant containing a power of two, which it is quite capable of doing. If there were to be alternate methods of accomplishing integer division which would result in different-than-standard results for integer division then that would warrant the addition of new IL opcodes to represent the intent with deterministic results.
Oct 1, 2014 at 5:31 PM
Halo_Four wrote:
supercat wrote:
Can you suggest any clean way of writing the code such that it would be no less efficient if SCALE happens to be a power of two, but would yield correct results even if SCALE were some other value?

The efficiency is not as important as the intent. Integer division is expected to behave in one manner so only one IL opcode was dedicated to it. The JIT certainly shouldn't be left to decide that another method might be faster if the outcome would not be the same.
It is not uncommon for a program to need to divide two numbers in such fashion as to produce a rounded quotient. If code needs to compute the rounded value of x/SCALE, can you offer any way of writing that which would, if SCALE happened to equal 8, be no slower than ((x>>2)+1)>>1, but which would work for any constant value of SCALE? My intent is to divide by SCALE, with rounding, efficiently. A rounded-discrete-division operator would make it possible to express that intention clearly. What clear means of expression would you suggest in the absence of an operator?
Oct 1, 2014 at 6:20 PM
supercat wrote:
Halo_Four wrote:
supercat wrote:
Can you suggest any clean way of writing the code such that it would be no less efficient if SCALE happens to be a power of two, but would yield correct results even if SCALE were some other value?

The efficiency is not as important as the intent. Integer division is expected to behave in one manner so only one IL opcode was dedicated to it. The JIT certainly shouldn't be left to decide that another method might be faster if the outcome would not be the same.
It is not uncommon for a program to need to divide two numbers in such fashion as to produce a rounded quotient. If code needs to compute the rounded value of x/SCALE, can you offer any way of writing that which would, if SCALE happened to equal 8, be no slower than ((x>>2)+1)>>1, but which would work for any constant value of SCALE? My intent is to divide by SCALE, with rounding, efficiently. A rounded-discrete-division operator would make it possible to express that intention clearly. What clear means of expression would you suggest in the absence of an operator?
I would suggest that your operator requires a corresponding opcode to express your clear intent to the JIT.
Oct 2, 2014 at 8:09 PM
Halo_Four wrote:
I would suggest that your operator requires a corresponding opcode to express your clear intent to the JIT.
Why would a special opcode be required? Why could not the compiler simply generate some CIL to perform the requested computation? If the dividend isn't a constant the code might be a little clunky, but it would probably be no worse than what a programmer who wanted truncated or rounded division would have to write anyway.