Double -> Integer

  • Thread starter Thread starter Armin Zingler
  • Start date Start date
Armin Zingler said:
Thank you, but as I underlined it in my first post, I want to convert to
Integer
without rounding. CInt does perform rounding. Rounding is unnecessary
after
calling Int().

How about using ILGenerator.Emit Method?
 
Am 11.01.2011 03:18, schrieb Nobody:
How about using ILGenerator.Emit Method?

I don't know how I can make the VB compiler emit IL code inline
using this method.
 
Am 11.01.2011 03:18, schrieb Nobody:
How about using ILGenerator.Emit Method?

I think this is for compiler builders making use of the AssemblyBuilder class
and so on.

(in short:
AssemblyBuilder.DefineDynamicModule.DefineType.DefineMethod.GetILGenerator)
 
Armin,

Math is not my topic, but if I understand you correct, you want for xyz as
results by using the code below 7 7 7

Dim x As Integer = CInt(7.9)
Dim y As Integer = CInt(7.8)
Dim z As Integer = CInt(7.1)

Strange is that the default banking rounding is in my opinion not used
because i get 8 8 7, can you check if I understand something wrong?

Cor

"Armin Zingler" wrote in message
Hi,

is there a function in the Framework that cuts the places
after the decimal point of a Double and converts to an Integer
_without_ performing any rounding (as Convert.ToInt32 does)?
I mean, what the IL code "conv.i4" does? ATM I'm using a function
that exactly does what I need in a C++ helper library:

static int ToInt32(double value)
{
return int(value);
};

But I want to get rid of it because a whole library for this
function is oversized and because this simple function should be
in the Framework, shouldn't it?
 
Cor,

Am 11.01.2011 09:26, schrieb Cor:
Armin,

Math is not my topic, but if I understand you correct, you want for xyz as
results by using the code below 7 7 7

You're right.
Dim x As Integer = CInt(7.9)
Dim y As Integer = CInt(7.8)
Dim z As Integer = CInt(7.1)

Strange is that the default banking rounding is in my opinion not used
because i get 8 8 7,

CInt only calls Convert.ToInt32. The latter is better described in the
documentation: http://msdn.microsoft.com/en-us/library/ffdk7eyz.aspx
can you check if I understand something wrong?

You got it right. The only problem I have is that Convert.ToInt32 does
two steps:

1. Convert to Integer (simply by cutting the decimal places: 7 7 7 in your example)
2. Add/Subtract 1 to correct the result depending on the decimal places.

I need a function that only does step 1 (which is very quick) because step 2
is time-intensive and it is not required because the decimal places are always
*.0000000. The C++ function that I wrote does only this. I have to (and I can)
live with my one-function helper library, but I'm still a little surprised that
this is not in the Framework.

Armin
 
Armin,

You mean something like this but then with another name?

Dim x As Integer = CInt(Fix(7.9))
Dim y As Integer = CInt(Fix(7.8))
Dim z As Integer = CInt(Fix(7.1))

Or instead of the Fix the Int if the negative and positive should stay?

http://msdn.microsoft.com/en-us/library/xh29swte(v=VS.90).aspx

And then that in one function, I don't assume it is easy to get that.

Cor


"Armin Zingler" wrote in message
Cor,

Am 11.01.2011 09:26, schrieb Cor:
Armin,

Math is not my topic, but if I understand you correct, you want for xyz as
results by using the code below 7 7 7

You're right.
Dim x As Integer = CInt(7.9)
Dim y As Integer = CInt(7.8)
Dim z As Integer = CInt(7.1)

Strange is that the default banking rounding is in my opinion not used
because i get 8 8 7,

CInt only calls Convert.ToInt32. The latter is better described in the
documentation: http://msdn.microsoft.com/en-us/library/ffdk7eyz.aspx
can you check if I understand something wrong?

You got it right. The only problem I have is that Convert.ToInt32 does
two steps:

1. Convert to Integer (simply by cutting the decimal places: 7 7 7 in your
example)
2. Add/Subtract 1 to correct the result depending on the decimal places.

I need a function that only does step 1 (which is very quick) because step 2
is time-intensive and it is not required because the decimal places are
always
*.0000000. The C++ function that I wrote does only this. I have to (and I
can)
live with my one-function helper library, but I'm still a little surprised
that
this is not in the Framework.

Armin
 
Armin Zingler said:
Am 11.01.2011 03:18, schrieb Nobody:

I think this is for compiler builders making use of the AssemblyBuilder
class
and so on.

(in short:
AssemblyBuilder.DefineDynamicModule.DefineType.DefineMethod.GetILGenerator)

If only you could do this:

Imports System.Reflection.Emit
Public Class Form1

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Dim d As Double = 7.9999999
Dim i As Integer
Dim o As ILGenerator

o = New ILGenerator
o.Emit(OpCodes.Conv_I4, d)

Console.WriteLine(i)

End Sub
End Class

But doesn't compile because of one line. You need to call GetILGenerator()
method instead of using New. I am seeing examples of creating methods in the
fly, and calling them, but this may introduce additional overhead that is
slower than the current alternative. I would search the web for "IL code
injection" to find if there is a solution.
 
Cor said:
Armin,

You mean something like this but then with another name?

Dim x As Integer = CInt(Fix(7.9))
Dim y As Integer = CInt(Fix(7.8))
Dim z As Integer = CInt(Fix(7.1))

Or instead of the Fix the Int if the negative and positive should stay?

http://msdn.microsoft.com/en-us/library/xh29swte(v=VS.90).aspx

And then that in one function, I don't assume it is easy to get that.

Int/Fix functions return the same data type as the input, so if you put 7.9
Double, you get 7 of type Double, and CInt would convert the 7 Double to 7
Integer, but it also runs the rounding routine, so it's still slow.
 
Cor,

Am 11.01.2011 18:16, schrieb Cor:
Armin,

You mean something like this but then with another name?

Dim x As Integer = CInt(Fix(7.9))
Dim y As Integer = CInt(Fix(7.8))
Dim z As Integer = CInt(Fix(7.1))

No, because CInt rounds. Once again, I don't want to round. :-)
Or instead of the Fix the Int if the negative and positive should stay?

No matter if I use Fix or Int, it doesn't make sense to use CInt
afterwards.
http://msdn.microsoft.com/en-us/library/xh29swte(v=VS.90).aspx

And then that in one function, I don't assume it is easy to get that.

It's easy. Look at my C++ function.


Armin
 
Am 11.01.2011 18:24, schrieb Nobody:
Int/Fix functions return the same data type as the input, so if you put 7.9
Double, you get 7 of type Double, and CInt would convert the 7 Double to 7
Integer, but it also runs the rounding routine, so it's still slow.

Exactly!
 
But this means that you don't want to use CInt at all, because Cint(7D) is
the same as Cint(Fix(7.9)) for the second part.

Then there are in my idea less options.

Or do you mean that they should change the internal code for CInt(double),
few chance on that in my idea as that gives breaking chances.

Cor

"Armin Zingler" wrote in message
Am 11.01.2011 18:24, schrieb Nobody:
Int/Fix functions return the same data type as the input, so if you put
7.9
Double, you get 7 of type Double, and CInt would convert the 7 Double to 7
Integer, but it also runs the rounding routine, so it's still slow.

Exactly!
 
And you really believe what you want is quicker than
Dim x = Cint(Fix(7.9999))

Every reflection method is slow in fact can Armin than simple use Option
Strict Of to obtain the same.

In my idea few chance on that.

:-)

Cor

"Nobody" wrote in message
Armin Zingler said:
Am 11.01.2011 03:18, schrieb Nobody:

I think this is for compiler builders making use of the AssemblyBuilder
class
and so on.

(in short:
AssemblyBuilder.DefineDynamicModule.DefineType.DefineMethod.GetILGenerator)

If only you could do this:

Imports System.Reflection.Emit
Public Class Form1

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Dim d As Double = 7.9999999
Dim i As Integer
Dim o As ILGenerator

o = New ILGenerator
o.Emit(OpCodes.Conv_I4, d)

Console.WriteLine(i)

End Sub
End Class

But doesn't compile because of one line. You need to call GetILGenerator()
method instead of using New. I am seeing examples of creating methods in the
fly, and calling them, but this may introduce additional overhead that is
slower than the current alternative. I would search the web for "IL code
injection" to find if there is a solution.
 
Am 11.01.2011 19:34, schrieb Cor:
But this means that you don't want to use CInt at all, because Cint(7D) is
the same as Cint(Fix(7.9)) for the second part.

Correct, I do not want to use CInt (or Convert.ToInt32).
Then there are in my idea less options.

Or do you mean that they should change the internal code for CInt(double),
few chance on that in my idea as that gives breaking chances.

No, they shouldn't change CInt (or Convert.ToInt32), but it would be nice
to have an additional function the does not perform unnecessary rounding.

Just take the whole part before the "." as an Integer. That's all. No magic.
Very simple. It's not expected _more_, it's expected _less_. :-)
 
Am 11.01.2011 19:37, schrieb Cor:
And you really believe what you want is quicker than
Dim x = Cint(Fix(7.9999))

Every reflection method is slow in fact can Armin than simple use Option
Strict Of to obtain the same.

In my idea few chance on that.

:-)

Well, it's an option. :) I'm gonna try.....
 
Armin,

this code

Dim x As Integer = CInt(Fix(7.9))
Dim y As Integer = Convert.ToInt32(Fix(7.9))

leads to this IL not so much in my idea.

..locals init (
[0] int32 x,
[1] int32 y)
L_0000: nop
L_0001: ldc.r8 7.9
L_000a: call float64
[Microsoft.VisualBasic]Microsoft.VisualBasic.Conversion::Fix(float64)
L_000f: call float64 [mscorlib]System.Math::Round(float64)
L_0014: conv.ovf.i4
L_0015: stloc.0
L_0016: ldc.r8 7.9
L_001f: call float64
[Microsoft.VisualBasic]Microsoft.VisualBasic.Conversion::Fix(float64)
L_0024: call int32 [mscorlib]System.Convert::ToInt32(float64)
L_0029: stloc.1

I assume also don't assume they have taken the Math round for nothing
instead of the convert.

Cor

"Armin Zingler" wrote in message
Am 11.01.2011 19:34, schrieb Cor:
But this means that you don't want to use CInt at all, because Cint(7D) is
the same as Cint(Fix(7.9)) for the second part.

Correct, I do not want to use CInt (or Convert.ToInt32).
Then there are in my idea less options.

Or do you mean that they should change the internal code for CInt(double),
few chance on that in my idea as that gives breaking chances.

No, they shouldn't change CInt (or Convert.ToInt32), but it would be nice
to have an additional function the does not perform unnecessary rounding.

Just take the whole part before the "." as an Integer. That's all. No magic.
Very simple. It's not expected _more_, it's expected _less_. :-)
 
Am 11.01.2011 20:21, schrieb Cor:
Armin,

this code

Dim x As Integer = CInt(Fix(7.9))
Dim y As Integer = Convert.ToInt32(Fix(7.9))

leads to this IL not so much in my idea.

..locals init (
[0] int32 x,
[1] int32 y)
L_0000: nop
L_0001: ldc.r8 7.9
L_000a: call float64
[Microsoft.VisualBasic]Microsoft.VisualBasic.Conversion::Fix(float64)
L_000f: call float64 [mscorlib]System.Math::Round(float64)
L_0014: conv.ovf.i4
L_0015: stloc.0
L_0016: ldc.r8 7.9
L_001f: call float64
[Microsoft.VisualBasic]Microsoft.VisualBasic.Conversion::Fix(float64)
L_0024: call int32 [mscorlib]System.Convert::ToInt32(float64)
L_0029: stloc.1

I assume also don't assume they have taken the Math round for nothing
instead of the convert.

Also look inside Convert.ToInt32. I've posted it already. Why so much
rounding code for nothing? Again, the decimal places are zero after
calling Fix. There is nothing to round.
 
Am 11.01.2011 18:20, schrieb Nobody:
But doesn't compile because of one line. You need to call GetILGenerator()
...


Works!

'Modified sample code from the documentation. (quick'n'dirty)

Imports System.Reflection
Imports System.Reflection.Emit

Public Class Form1
Private Delegate Function ToInt32Delegate(ByVal value As Double) As Integer

Private Shared Function CreateMyPersonalToInt32() As ToInt32Delegate

Dim aName As New AssemblyName("DynamicAssemblyExample")
Dim ab As AssemblyBuilder = _
AppDomain.CurrentDomain.DefineDynamicAssembly( _
aName, _
AssemblyBuilderAccess.Run)

Dim mb As ModuleBuilder = ab.DefineDynamicModule(aName.Name)
Dim tb As TypeBuilder = mb.DefineType("MyDynamicType", TypeAttributes.Public)

Dim parameterTypes As Type() = {GetType(Double)}
Dim methodBuilder = tb.DefineMethod("ToInt32", _
MethodAttributes.Static Or MethodAttributes.Public, GetType(Int32), parameterTypes)

Dim Generator As ILGenerator = methodBuilder.GetILGenerator()

Generator.Emit(OpCodes.Ldarg_0)
Generator.Emit(OpCodes.Conv_I4)
Generator.Emit(OpCodes.Ret)

Dim t As Type = tb.CreateType()

Dim mi As MethodInfo = t.GetMethod("ToInt32", BindingFlags.Public Or BindingFlags.Static)

Return DirectCast([Delegate].CreateDelegate(GetType(ToInt32Delegate), t, "ToInt32"), ToInt32Delegate)

End Function

Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)

Dim MyConversionDelegate = CreateMyPersonalToInt32()

Dim i As Integer = MyConversionDelegate(7.9)


End Sub
End Class


:-)

Thank you for the hint! You know, I did this just out of interest.
 
Armin Zingler said:
Am 11.01.2011 18:20, schrieb Nobody:

Works!

It would be interesting to see if there is any overhead that makes it as
slow as the alternatives that you have tried, and compared to the overhead
in calling the DLL function.
 
Am 11.01.2011 21:28, schrieb Nobody:
It would be interesting to see if there is any overhead that makes it as
slow as the alternatives that you have tried, and compared to the overhead
in calling the DLL function.

I did some performance tests. Before posting the source and the results,
I wanted to make the code look neater. With the "redesigned" version, I
got different results even though it does exactly the same. Although I'm
aware of the side-effects of optimizations, there seem to be several
influences that don't allow me to post robust figures.

Despite, and only for the sake of completeness, I post the source, so you
can play with and modify it and make your own measure experiences. I don't
comment it now.

For my part, these files can be closed now. :-)


Option Infer On
Option Strict On

Imports Microsoft.VisualBasic
Imports System
Imports System.Diagnostics
Imports System.Reflection
Imports System.Reflection.Emit

Friend Class Main
Private Delegate Function ToInt32Delegate(ByVal value As Double) As Integer

Private Shared ReadOnly ConversionDelegate As ToInt32Delegate = CreateConversionDelegate()
Private Shared D As Double = 7.9R
Private Shared i, j As Integer

Shared Sub Main()

Dim duration As TimeSpan

duration = RunTest()
MsgBox(duration.ToString)

duration = RunTest()
MsgBox(duration.ToString)

End Sub
Private Shared Function RunTest() As TimeSpan

Dim watch As Stopwatch

watch = Stopwatch.StartNew

For Project1.Main.i = 1 To 1000000000
' optimized not optimized
'Project1.Main.j = CInt(D) '23.3 16.3
'Project1.Main.j = ConversionDelegate(D) ' 6.9 14.5
'Project1.Main.j = DoubleToIntTableHelper.Main.ToInt32(D) ' 5.6 16.6
'Project1.Main.j = Convert.ToInt32(D) '11.9 22.0
Next

watch.Stop()
Return watch.Elapsed

End Function
Private Shared Function CreateConversionDelegate() As ToInt32Delegate

Dim AssyName As New AssemblyName("ConversionHelper")
Dim AssyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(AssyName, AssemblyBuilderAccess.Run)
Dim ModuleBuilder = AssyBuilder.DefineDynamicModule(AssyName.Name)
Dim TypeBuilder = ModuleBuilder.DefineType("Main", TypeAttributes.Public)
Dim ParameterTypes As Type() = {GetType(Double)}
Dim MethodBuilder = TypeBuilder.DefineMethod( _
"ToInt32", MethodAttributes.Static Or MethodAttributes.Public, GetType(Int32), ParameterTypes)
Dim Generator As ILGenerator = MethodBuilder.GetILGenerator()
Dim t As Type

Generator.Emit(OpCodes.Ldarg_0)
Generator.Emit(OpCodes.Conv_I4)
Generator.Emit(OpCodes.Ret)

t = TypeBuilder.CreateType()

Return DirectCast( _
[Delegate].CreateDelegate(GetType(ToInt32Delegate), t, MethodBuilder.Name), _
ToInt32Delegate _
)

End Function


End Class
 
Back
Top