Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

Convert double to integer?


  • Please log in to reply
9 replies to this topic

#1 Basiclife

Basiclife

    Member

  • Members
  • PipPip
  • 27 posts

Posted 10 January 2012 - 03:56 PM

I've got an RGB LED plugged into 3 of the PWM outputs. I want to cross-fade between colours.

I thought this would be trivial but apparently not. I've simplified this to just be a single output for now.

    Dim Start As UInteger = 0
    Dim [End] As UInteger = 255
    Dim Steps = 100

    For x = 1 To Steps
        Dim Temp = ([End] - Start) / Steps * x + Start
        Dim R = CUInt(Temp)

        Debug.Print(x.ToString & ": Temp: " & Temp.ToString & ", R:" & R.ToString)
        LedR.SetPulse(255, R)
        Threading.Thread.Sleep(20)
    Next

This outputs:
1: Temp: 2.5500, R:196608
2: Temp: 5.1000, R:327680
3: Temp: 7.6500, R:524288
4: Temp: 10.2000, R:655360
5: Temp: 12.7499, R:851968
...
95: Temp: 242.2488, R:15859712
96: Temp: 244.7988, R:16056320
97: Temp: 247.3488, R:16187392
98: Temp: 249.8988, R:16384000
99: Temp: 252.4488, R:16515072
100: Temp: 254.9988, R:16711680

As you can see the actual calculation works perfectly but I can't find ANY way to shoe-horn the resulting double into a UInt.

NB: In this scenario, I don't care at all about precision so Ceil/Floor/Truncate are all perfectly acceptable.

I've tried:
        Dim R = CUInt(Temp) 'Odd output - maybe reading memory and just treating it as a UInt
        Dim R = CType(Temp, UInteger) 'Throws: An unhandled exception of type 'System.Exception' occurred in Netduino_VBTest.exe
        Dim R = Convert.ToUInteger(Temp) 'Doesn't have an overload for Double (or anything except string!)
        Dim R = Convert.ToUInteger(Temp.ToString) 'Throws an exception

Any suggestions appreciated.

#2 Paul Newton

Paul Newton

    Advanced Member

  • Members
  • PipPipPip
  • 724 posts
  • LocationBerkshire, UK

Posted 10 January 2012 - 06:22 PM

Hi.

I don't code in Visual Basic, (and am still quite new to C# for that matter), but in C/C++ I would do the whole calculation in integer arithmetic.

The trick is to multiply first then divide so that you don't lose any precision.

Pseudo code:
constant integer start = 0;
constant integer end = 255;
constant integer steps = 100;

integer x;
integer result;

for (x = 1 to 100)
{
    result = ((x * (end-start)) / steps) + start;
}

pick a few values:
x = 1

1 * 255 = 255, 255 / 100 = 2, 2 + 0 = 2,
result = 2

x = 5

5 * 255 = 1275, 1275 / 100 = 12, 12 + 0 = 12,
result = 12

x = 10

10 * 255 = 2550, 2550 / 100 = 25, 25 + 0 = 25,
result = 25

x = 13

13 * 255 = 3315, 3315 / 100 = 33, 33 + 0 = 33
result = 33

x = 95

95 * 255 = 24225, 24225 / 100 = 242, 242 + 0 = 242
result = 242

x = 100

100 * 255 = 25500, 25500 / 100 = 255, 255 + 0 = 255,
result = 255


Might be of some help.

Paul

#3 baxter

baxter

    Advanced Member

  • Members
  • PipPipPip
  • 415 posts

Posted 10 January 2012 - 06:50 PM

Just declare and type your variables. This is always good practice.
Sub Main()
        Dim Start As UInteger = 0
        Dim [End] As UInteger = 255
        Dim Steps = 100

        'Declare your vars
        '***********************
        Dim R As UInteger = 0
        Dim temp As Double = 0.0
        '***********************

        For x = 1 To Steps
            temp = ([End] - Start) / Steps * x + Start
            R = CUInt(temp)

            Debug.Print(x.ToString & ": Temp: " & Temp.ToString & ", R:" & R.ToString)
            'LedR.SetPulse(255, R)
            'Threading.Thread.Sleep(20)
        Next

        'Output
        '======

        '95: Temp: 242.24999999999997, R:242
        '96: Temp: 244.79999999999998, R:245
        '97: Temp: 247.34999999999999, R:247
        '98: Temp: 249.89999999999998, R:250
        '99: Temp: 252.44999999999999, R:252
        '100: Temp: 254.99999999999997, R:255


    End Sub
Baxter

#4 Magpie

Magpie

    Advanced Member

  • Members
  • PipPipPip
  • 279 posts
  • LocationAustralia (south island)

Posted 11 January 2012 - 01:08 AM

Could you convert your code to c#?

Then you could use

int i = (int)System.Math.Round(1230.98);

I don't know a .net micro vb translation of this as I haven't got VB on this computer,
but the C# version works.

Or maybe create a c# library project that your vb code can call, then put in this method.

namespace misc.random.libs.workarounds
{
    public class utils
    {
        public static int ConvertToInt(double input)
        {
            return (int)System.Math.Round(input);
        }
    }

}

STEFF Shield High Powered Led Driver shield.

#5 Basiclife

Basiclife

    Member

  • Members
  • PipPip
  • 27 posts

Posted 11 January 2012 - 02:08 PM

Thanks all for your answers, I'll respond individually...

The trick is to multiply first then divide so that you don't lose any precision.
...

Might be of some help.

Paul

Thanks, that's a reasonable approach to take - especially since I'm not concerned with precision. The problem is that I like to code with Option Strict on which means the conversion you perform which rounds to the nearest integer (result=) is a narrowing conversion, resulting in an Error/Warning. This is same of vanilla .Net but in vanilla, I'd use CInt() to explicitly inform the compiler that I'm expecting the narrowing conversion to take place, removing the error/warning and also reducing coding errors by preventing inadvertent conversion from double to int.

Just declare and type your variables. This is always good practice.Baxter


Thanks for the suggestion. I suspect you ran your test using the emulator not an actual netduino? I get the correct output using the emulator too. Running your code against the netduino shows exactly the same problem as my example:

1: Temp: 2.5500, R:196608
2: Temp: 5.1000, R:327680
3: Temp: 7.6500, R:524288

(If you ran it against your netduino, which one and what firmware?)

I have to say I disagree with you re: typing variables. Implicit typing is perfectly acceptable as it's done at design time and is simply a shorthand notation (as opposed to say PHP with mixed variable types). You can verify this by hovering over the variable in the editor - it will tell you what the type is and won't allow you to treat it as anything else. Using a variable before it's initialised is bad practice but that's why VS gives you a warning.

One other reason I prefer my approach is that variables declared inside the loop are out-of-scope once you leave the loop so are eligible for GC. It also means I won't refer to them by mistake as the designer knows they're no longer valid.

Although I haven't been able to achieve it yet with the MF, all my normal .Net apps have 0 warnings (I treat warnings as errors and TFS won't allow a code checkin unless it compiles and passes unit tests). This tends to enforce good coding habits and has served me well so far - If anyone has reasons they believe I'm wrong, let me know as I'd love to discuss coding style improvements.

Could you convert your code to c#?



Hi Magpie - I could indeed write this in C# (and I use C# at work more often than not) but I personally prefer VB and this is my first noddy project to test VB support - needless to say, I was amazed that you can't do the conversion. Incidentally, Math.Round seems to be available in VB too but it still returns a double (even though it doesn't have a param to indicate precision). As such, I just get an integer stored as a double and the same problems happen again.



I've found a workaround but it's such an ugly hack and I don't recommend anyone use it for any purpose...

Dim Test = 123.45
Debug.Print(UInteger.Parse(Test.ToString.Split("."c)(0)).ToString)

*shudder*

NB:
UInteger.Parse("123") works fine
UInteger.Parse("123.0") throws an exception


#6 Paul Newton

Paul Newton

    Advanced Member

  • Members
  • PipPipPip
  • 724 posts
  • LocationBerkshire, UK

Posted 11 January 2012 - 02:23 PM


Thanks, that's a reasonable approach to take - especially since I'm not concerned with precision. The problem is that I like to code with Option Strict on which means the conversion you perform which rounds to the nearest integer (result=) is a narrowing conversion, resulting in an Error/Warning. This is same of vanilla .Net but in vanilla, I'd use CInt() to explicitly inform the compiler that I'm expecting the narrowing conversion to take place, removing the error/warning and also reducing coding errors by preventing inadvertent conversion from double to int.


If I was working in C/C++ there would not be any rounding or conversion; this is because all of the variables are of type integer. The whole thing gets done in integer arithmetic; any fractional part generated by the divide is just lost.

Does VB convert to floating point to do the calculation when you use the multiply and divide operators - thus requiring a conversion?

Paul

#7 Basiclife

Basiclife

    Member

  • Members
  • PipPip
  • 27 posts

Posted 11 January 2012 - 03:09 PM

If I was working in C/C++ there would not be any rounding or conversion; this is because all of the variables are of type integer. The whole thing gets done in integer arithmetic; any fractional part generated by the divide is just lost.

Does VB convert to floating point to do the calculation when you use the multiply and divide operators - thus requiring a conversion?

Paul

Exactly so

Dim I as Integer
I = 3/2

or 

Dim I, J as Integer
J = 4
I = J/2

or even

Dim I as Integer
I = 1.5


Would all produce a warning (Implicit conversion from Double to Integer)

You can, of course, disable the warning but I tend to like it as it's enforcing strong typing rules. If I intended to force it into an int, I'd do:

Dim I as Integer
I = CInt(3/2)

CInt takes a double (amongst other things) and returns an integer thus complying with strong typing, performing the truncation for me and also providing a level of confidence that I'm not treating double as integers and losing precision unless I explicitly intend to do so.


EDIT: You've just reminded me of the integer divison operator which I rarely ever use...
7 / 2     produces 3.5
7 \ 2     produces 3

With your rearranged formula, this might do the trick. Let me have a play...

Ok, Using:

Dim R = ([End] - Start) * x \ Steps + Start

Seems to work as I'd expect - Thank You!

(Incidentally, it technically results into a long but Long-UInt works without issue and I shouldn't ever overflow)

This does solve the immediate problem but I still think the fundamental question of converting a double to an int is an important one - perhaps it's just been too long since I played with PICs and I've got set in my comfy desktop cpu ways.

#8 Paul Newton

Paul Newton

    Advanced Member

  • Members
  • PipPipPip
  • 724 posts
  • LocationBerkshire, UK

Posted 11 January 2012 - 05:33 PM

Glad its sorted. / & \ Brings back memories of incompatible UNIX & DOS path names! Paul

#9 baxter

baxter

    Advanced Member

  • Members
  • PipPipPip
  • 415 posts

Posted 11 January 2012 - 06:49 PM

Hi Basiclife,

This is very strange. After reading your comment, I re-ran the code that I submitted and got the same answer; and yes, I deployed to Netduno not the emulator (Project Properties, transport:USB, Device:NetduinoPlus_NetduinoPlus). Also, I did not run your code initially. So I ran your original code to get:
93: Temp: 237.14999999999998, R:237
94: Temp: 239.69999999999999, R:240
95: Temp: 242.24999999999997, R:242
96: Temp: 244.79999999999998, R:245
97: Temp: 247.34999999999999, R:247
98: Temp: 249.89999999999998, R:250
99: Temp: 252.44999999999999, R:252
100: Temp: 254.99999999999997, R:255
I am running MF 4.2 RC1 with Netduino Plus. I never ugraded to RC3 because of all of the problems noted in the forum. I seem to recall that there were other issues with doubles in RC3. I guess this is just another bug in RC3 (e.g. Dim R = CUInt(Temp)). You have a good point about scope.

Baxter

#10 Basiclife

Basiclife

    Member

  • Members
  • PipPip
  • 27 posts

Posted 11 January 2012 - 07:23 PM

Hi Basiclife,

This is very strange. After reading your comment, I re-ran the code that I submitted and got the same answer; and yes, I deployed to Netduno not the emulator (Project Properties, transport:USB, Device:NetduinoPlus_NetduinoPlus). Also, I did not run your code initially. So I ran your original code to get:

93: Temp: 237.14999999999998, R:237
94: Temp: 239.69999999999999, R:240
95: Temp: 242.24999999999997, R:242
96: Temp: 244.79999999999998, R:245
97: Temp: 247.34999999999999, R:247
98: Temp: 249.89999999999998, R:250
99: Temp: 252.44999999999999, R:252
100: Temp: 254.99999999999997, R:255
I am running MF 4.2 RC1 with Netduino Plus. I never ugraded to RC3 because of all of the problems noted in the forum. I seem to recall that there were other issues with doubles in RC3. I guess this is just another bug in RC3 (e.g. Dim R = CUInt(Temp)). You have a good point about scope.

Baxter


Hi Baxter,

Thanks for the info - I think you must be right re: being a bug in RC3 (which is what I'm running). I'll wait until the next release and see how it goes.

Thanks also for the confirmation that my code works on your hardware - good to know I'm not being a complete idiot :)




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.