Shop OBEX P1 Docs P2 Docs Learn Events
Maffs is hard - propeller style — Parallax Forums

Maffs is hard - propeller style

pacmanpacman Posts: 327
edited 2013-03-27 14:02 in Propeller 1
Why ?

11 x 3.5

(to most normal people this would be 38.5. Heck, I would accept an answer of 38 or 39.

but in the prop it seems to give an answer of -1004534808

snippet of code below.
PST.newline
   PST.dec(11 * 3.5)

Is it some sort of wierd overflow thing (but the result is WAY less than a 32 bit number)? Something to do with trying to mush a floating point into the prop?

I'm confused - please explain.

I do notice that
PST.newline
   PST.dec(11 * 35 / 10)

gives a much closer answer.

What am I miss-thinking?

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2013-03-25 01:59
    pacman wrote: »
    PST.dec(11 * 3.5)
    Eleven is an integer, 3.5 OTOH is considered a floating point number (encoded as $40600000). Then you do an integer multiply which gives you $4060_0000 * $B = $2_C420_0000. This is truncated to $C4200000 and results in -1004535808 when printed as decimal.
  • Heater.Heater. Posts: 21,230
    edited 2013-03-25 02:37
    In short the Propeller and Spin language work with integers. That is to say signed whole numbers that fit into 32 bits. (Lets skip BYTE, WORD) for now.

    So 10 / 3 can only be 3 because there is no way to represent 0.333333.....

    Although you can use // to get the remainder of a division which will be 1 in this case.

    However Spin does allow you to write real numbers like 2.3 in which case it encodes them in floating point format as kuroneko points out. This is in case you want to use one of the objects that does floating pint arithmetic, F32 for example. Or in case Spin gets floating point types at some point in the future I guess.

    You cannot mic up floating point numbers with normal integers in your expressions.

    You can always scale your numbers up and then work with them. So for example scaling all your numbers by 10 will give your first example as (110 * 35) which will get you a result of 3850. Scale that back down by 10 and you have 385 which is the 38.5 answer you wanted scaled by 10.

    Choose your scaling to get you the precision you want and be careful of overflowing with big numbers.
  • lardomlardom Posts: 1,659
    edited 2013-03-25 07:15
    kuroneko wrote: »
    Eleven is an integer, 3.5 OTOH is considered a floating point number (encoded as $40600000). Then you do an integer multiply which gives you $4060_0000 * $B = $2_C420_0000. This is truncated to $C4200000 and results in -1004535808 when printed as decimal.
    "3.5" is represented by eight digits! That seems like a lot. Where can I learn more about how floating point numbers are encoded?
  • Martin_HMartin_H Posts: 4,051
    edited 2013-03-25 07:43
    Floating point constants coupled with Spin's typeless nature are one of the nastier lurking gotchas. I've written inverse kinematics in both Spin and C++ and I got bit by that gotcha several times.
  • Heater.Heater. Posts: 21,230
    edited 2013-03-25 08:35
    lardom,

    Google is your friend: http://en.wikipedia.org/wiki/Single-precision_floating-point_format

    Basically the format has a sign bit in the top bit. Followed by 8 bits of exponent. Followed by 23 bits of fraction.

    Things get a bit complicated because the sign bit is not used as it is in twos comp. If it's set your number is negative so you can have both plus zero and minus zero. The exponent is a biased 8 bit value representing a range -128 to +127. It is a biased value such that 127 is actually zero.

    Then we have problems with normalized numbers. Normalized means that the number is always shifted up to be of the form 1.xxxx But as the first digit is always one we don't need to include it in the representation. So 0.1 binary will comeout with the 1 bit up un the 22nd bit position of a 32 bit word.

    Of course not all numbers are normalized....

    Then we have to represent infinity and Not A Number (NaN) which have special encodings.

    It all gets very complicated.
  • LeonLeon Posts: 7,620
    edited 2013-03-25 11:33
    lardom wrote: »
    "3.5" is represented by eight digits! That seems like a lot. Where can I learn more about how floating point numbers are encoded?

    It's only four bytes or 32 bits and the Propeller is a 32-bit device! It's the standard for single-precision arithmetic.
  • pacmanpacman Posts: 327
    edited 2013-03-25 15:05
    Thanks for all the info guys.

    I used to having to do 'scaling' in my normal day-to-day job working with PLC's so it all makes sense

    Why I didn't twig to the propellor is a bit of a mystery - perhaps it's that I could enter a decimal value - most PLCs (when woring in 'integer space' dont let you put in the '.' so it's a bit more obvious).
  • Mark_TMark_T Posts: 1,981
    edited 2013-03-26 05:11
    lardom wrote: »
    "3.5" is represented by eight digits! That seems like a lot. Where can I learn more about how floating point numbers are encoded?

    You might be interested to hear about things called "search engines" - they are very handy for this kind of enquiry ;)
  • lardomlardom Posts: 1,659
    edited 2013-03-26 09:50
    Heater, thanks for the link. So it's called 'single point floating point format'.
  • Heater.Heater. Posts: 21,230
    edited 2013-03-26 10:07
    That would be "Single-precision floating-point format"
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2013-03-26 10:11
    There is another method that can be used here, and since you mentioned scaling, I thought I would present this. This method is useful when you don't have the overhead for handling floating point but still want to maintain a reasonable amount of accuracy.

    Essentially it is a binary search tree. In your example you want to multiply 11 by 3.5 ... .

    Since a binary search tree requires a reference value for LOW and HIGH, we can set the LOW to 0, but for the HIGH we must calculate the value.

    RefHIGH = ( 1024 / 3.5 ) = 292

    ... Ok, I cheated a bit, the RefHIGH rolls the scaling of 3.5 that we want to use into the equation with a 10 -bit (That's the 1024) resolution for the output.
    CON
    
      _CLKMODE = XTAL1 + PLL16X
      _XINFREQ = 5_000_000
    
    
    OBJ
    
    PST           : "Parallax Serial Terminal"
    
    
    VAR
    
    long    Data
    
    PUB start
        PST.Start(19200{<- Baud})
    
        RefHIGH                     := 292                       
        RefLOW                      := 0
        Data                        := 11
        BitRes                      := 10
        cognew(@SAN, @Data)
    
        'Note: input Data gets re-written with the result
    
    
        repeat
          PST.bin(Data,10)
          PST.Char(9{<- TAB character})
          PST.dec(Data)
          PST.Char(13{<- Return character})
    
    
    DAT
            org             0
    SAN'routine     '(SAN) Successive Approximation BASE-2 Normalization
            rdlong          _Data,                  par                             'Read Data value
            mov             t1,                     #0                              'Clear t1
            mov             Counter,                BitRes
    BitLoop
    '----------------------------------------------------------------------------------------------------------
            mov             RefMID,                 RefHIGH                         'RefMID = (RefHIGH + RefLOW ) / 2
            add             RefMID,                 RefLOW
            shr             RefMID,                 #1
            cmp             RefMID,                _Data    wc                      'Compare Data and RefMID
       if_c mov             RefLOW,                 RefMID                          'if Data >  RefMID ; RefLOW = RefMID
      if_nc mov             RefHIGH,                RefMID                          'if Data <= RefMID ; RefHIGH = RefMID
            rcl             t1,                     #1                              'Rotate "C" into t1's LSB
            djnz            Counter,                #BitLoop                        'Get next Bit
    '----------------------------------------------------------------------------------------------------------        
            wrlong          t1,                     par                             'Place result in Data
            Cogid           t1                                                      'Stop COG
            cogstop         t1
    
    Counter       long      0
    t1            long      0
    _Data         long      0
    BitRes        long      0
    RefHIGH       long      0
    RefLOW        long      0
    RefMID        long      0
    

    The above will produce an output of 39 if the input value is 11

    So why 10-bits? ... If you multiply your input by 16 , so 11 becomes 176 then the code above produces a value of 619 on the output. The lowest range that 619 can 'fit' is in a 10-bit value. Looking at it this way if you shift the result right by 4 you effectively divide by 16 and you will see 38 as a result, but if you also look at the lower nibble (4-bits) first, then the value is 11 ... or 11/16ths ....or 0.6875 .... combine that with the 38 and you have 38.6875 .... a crude floating point if you will. The reason that it's not exactly 38.5 is because of truncating and bit level repeating mentioned earlier.
  • prof_brainoprof_braino Posts: 4,313
    edited 2013-03-27 14:02
    Mark_T wrote: »
    You might be interested to hear about things called "search engines" - they are very handy for this kind of enquiry ;)

    Search engines don't operate well without the proper search terms. When a people asks for help as above, its typically because they do not know the proper search terms. Responding with a link or the proper search terms (as heater did) is useful; your response was not.

    Sorry, but this just bugs me and I see it often. We prefer folks to ask trivial questions rather than make extreme mistakes. Discouraging questions is no longer considered "best practice".
Sign In or Register to comment.