Shop OBEX P1 Docs P2 Docs Learn Events
Elegant overflow detection in Spin2? — Parallax Forums

Elegant overflow detection in Spin2?

More newbie stuff here, but searching the fora didn't find much useful (my search-fu is not strong), so...

Given a and b, both unsigned 32-bit integers, is there an elegant way in Spin2 to detect that an overflow (or underflow) has occurred? Obviously, one could do something like this:


a_old := a
a_new := (a + b)
If a_new < a_old
'' overflow occurred, do something
else
'' overflow did not, do something else
a := a_new

but it seems a touch inelegant, requiring extra variables and all. Perhaps this is a job for PASM? S.

«1

Comments

  • cgraceycgracey Posts: 14,151

    Maybe:

    if a + b < a
      'overflow
    else
      a += b
    
  • RaymanRayman Posts: 14,632

    Can you do the add and then get the carry flag via inline assembly?

  • Note that to detect unsigned integer overflow you'll want to use unsigned comparisons. Also, a and b are symmetric, so if they are both treated as unsigned I think you could do:

      a += b
      if a +< b
        ' overflow
    
  • ScroungreScroungre Posts: 161
    edited 2021-03-17 18:47

    Interesting ideas, all. Thanks! One thing I wasn't real clear on at the outset is that I still want the 'overflowed remainder' in 'a', so testing before the operation doesn't help.

    Another way would be simply push them both to 31 bits, and do "if a >= $8000_0000". It's not exactly essential that I need four billion values, but they'd be nice.

    But yeah, I think the best way is PASM. S.

  • ScroungreScroungre Posts: 161
    edited 2021-03-17 18:48

    And that cgracey's solution would also work fine with the else clause added to the if clause. Or something... :) ha! Thank you.

  • Click the gear icon in the upper-right corner of the post you want to edit.

  • I think it would be handy to redefine $8000_0000 in Spin2 as NaN and $8000_0001 as NEGX. That would allow any operation that causes an overflow to result in NaN and any operation where one of the operands is NaN to return NaN. The checking could be made optional with a pragma.

    -Phil

  • Not sure I understand you there.

    You'd need to throw in a bitwise operation on that, else $7FFF_FFFF + $0000_0003 == $8000_0002 - Causing both a 31-bit overflow yet not being equal to your defined NaN or NEGX.

    Furthermore, $8000_0000 + $8000_0000 would equal $0000_0000, zero, not NaN, despite both operands being NaN.

    Finally, that makes it fundamentally a 31-bit machine, not 32. That extra bit could come in handy! S.

  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2021-03-18 01:14

    You'd need to throw in a bitwise operation on that, else $7FFF_FFFF + $0000_0003 == $8000_0002 - Causing both a 31-bit overflow yet not being equal to your defined NaN or NEGX.

    The idea is that the interpreter would detect the overflow and intervene to set the result to NaN.

    Furthermore, $8000_0000 + $8000_0000 would equal $0000_0000, zero, not NaN, despite both operands being NaN.

    Again, it's a matter of the interpreter intervening to set the result of any operation that has NaN as an operand to NaN.

    Finally, that makes it fundamentally a 31-bit machine, . . .

    No, it doesn't. It's still a fully 32-bit machine, with the exception of one value: $8000_0000.

    -Phil

  • Hm. Okay, I think I better understand what you're thinking, but if so I disagree with you.

    See, $8000_0000 is a perfectly valid number in other ways. I think you might have a difficult time explaining why:

    2,147,483,645 + 1 = 2,147,483,646
    2,147,483,646 + 1 = 2,147,483,647
    2,147,483,647 + 1 = NaN
    2,147,483,648 + 1 = NEGX (actually NaN, because one operand is NaN*)
    2,147,483,649 + 1 = 2,147,483,650 (this works fine...)
    2,147,483,650 + 1 = 2,147,483,651
    ... and so on.

    The testing involved to ensure those specific numbers get skipped in 32-bit math (or merely a cheery 32-bit counter!) would be fairly frightening. I think the interpreter would be better off not trying to use 'special' values, or if so, at least use the ones at the very end of the range(s). S.

    • But 2,147,483,647 + 2 = NEGX (and not NaN) Imagine trying to debug that...
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2021-03-18 02:07

    See, $8000_0000 is a perfectly valid number in other ways.

    So what? Once it's taken out of circulation as a valid number by the interpreter, it becomes an exception and is dealt with as such.

    But 2,147,483,647 + 2 = NEGX (and not NaN) Imagine trying to debug that...

    Nothing to debug. That causes an overflow, which the interpreter will intervene to assign the result NaN.

    Here's the logic:

    if (a == NaN or b == NaN)
      result = NaN
    elseif (result := a op b causes an overflow) then
      result = NaN
    

    -Phil

  • @"Phil Pilgrim (PhiPi)" said:

    See, $8000_0000 is a perfectly valid number in other ways.

    So what? Once it's taken out of circulation as a valid number by the interpreter, it becomes an exception and is dealt with as such.

    But 2,147,483,647 + 2 = NEGX (and not NaN) Imagine trying to debug that...

    Nothing to debug. That causes an overflow, which the interpreter will intervene to assign the result NaN.

    But that's NOT a 32-bit overflow. That's a 31-bit overflow! We're being unsigned here!

    Here's the logic:

    if (a == NaN or b == NaN)
      result = NaN
    elseif (result := a op b causes an overflow) then
      result = NaN
    

    -Phil

    And it should be a valid number. I'm trying to build a system with accumulators here, and assuming I add to them numbers from 0-999, then about one in a thousand times I'm going to land directly on $8000_0000 and from then on my program is going to completely blow up because all the numbers added to the accumulator afterwards are considered 'NaN'? That would be Microsoft-levels of stupidity (and reliability). No. S.

  • But that's NOT a 32-bit overflow. That's a 31-bit overflow! We're being unsigned here!

    Who said anything about unsigned? My whole premise is predicated upon signed arithmetic.

    -Phil

  • ScroungreScroungre Posts: 161
    edited 2021-03-18 02:42

    Um, I did, in post #1 of this very thread. S.

    ETA: Signed 32-bit values are 31 bits in magnitude, and I pointed out that's probably acceptable but that I'd rather have 32. I go to some efforts in my binary math not to use signed values - saves a lot of hassle, imho.

  • And also Eric’s response in Post #4.

  • 'Shoulda been wearing my glasses. :)

    Anyway, I stand by my suggestion regarding signed arithmetic.

    -Phil

  • ScroungreScroungre Posts: 161
    edited 2021-03-18 03:39

    I had my eyeballs zapped by the laser guys about thirty years ago*, and they told me that in thirty years I'd need glasses again. They were right.

    You're not wrong - I just loathe signed binary math. Who was the wingnut who decided that the Unix timestamp of seconds from the epoch (1970) should be a signed integer? Were they just being careful in case Superman or the NCC 1701 Enterprise would turn back time? Nitwits... :p Just pick a good floor, and make that zero. If you're doing temperature, do it in Kelvin (the thermodynamics math makes more sense that way too). Et cetera.

    Given signed math, your choice of $8000_0000 is an 'edge value', and not a bad choice. Still, I'm going to object to valid values being re-defined. So there. :p

    Have fun! S.

    • Edited because I forgot the footnote - I spent about USD2400.00 (thirty years ago) on getting my eyes done, and it was about the best money I ever spent. Cannot recommend too highly. Was awesome.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2021-03-18 06:21

    Given signed math, your choice of $8000_0000 is an 'edge value', and not a bad choice. Still, I'm going to object to valid values being re-defined.

    "Valid" is rather relative. After all, you can't even negate $8000_0000. So it's pretty useless as a numerical quantity. That applies not only to twos-complement, but also to signed-magnitude, where $8000_0000 equals -- of all things -- minus zero! 'Might as well give it another job to do!

    -Phil

  • cgraceycgracey Posts: 14,151
    edited 2021-03-18 07:05

    That $8000_0000 is a bit of an anomaly in signed-integer math. I think ignoring it is the best approach.

    I could see making signed add and subtract instructions at the machine level report $8000_0000 if $8000_0000 was in either term, but doing this in software would just slow things down.

  • ScroungreScroungre Posts: 161
    edited 2021-03-18 20:41

    Back on topic here... This is ersmith's idea, basically word-for-word, and it works! Yay!

    With the numbers given it overflows in about 22 seconds, which is in fact a bit dismaying. The loop repeats at about 750kHz. I had hoped it would be an order of magnitude faster. By default, is it sharing a cog with the Spin interpreter? If so, would it be faster in its own?

    Or time to get started with PASM, I guess! (Again, total newbie here - there may be other obvious ways to make it faster too. Taking out the LED array pinwrite improves the speed quite a bit, but it's fun to watch and I have to do useful things with the results anyhow eventually). S.

    Edited to fix the uploaded file. It didn't get saved while I was tinkering with it, so the original 'Hello Blinky' that I edited it from didn't get overwritten. Ooops...

  • Your OFlowTesting.spin2 doesn't seem to do anything other than blinking an LED. What's overflowing?

  • The value 'a' overflows an unsigned 32-bit accumulator (itself), and when it does, it toggles the LED - so I can see it happen, mostly. The pinwrite to the LED array shows the high bits of the accumulator. In a way, I've simply implemented a 33-bit accumulator... But by looking at the high bits of 'a', I can watch them count up in a cheery binary manner, and when the counter wraps, the overflow LED toggle fires. Which is what I wanted it to do.

    It's just proof of concept (and a bit of speed testing). S.

  • I think you posted the wrong file -- there's no variable a in the OFlowTesting.spin2 in post #21, nor is there any math at all that I can see.

  • ScroungreScroungre Posts: 161
    edited 2021-03-18 20:43

    Oooops - yes, that is the wrong file. My bad! Editing... Should be correct file now.

  • @cgracey said:
    I could see making signed add and subtract instructions at the machine level report $8000_0000 if $8000_0000 was in either term, but doing this in software would just slow things down.

    Yes, it would definitely be a performance burden. That's why I suggested it be optional, enabled by a pragma. Perhaps its best use would be for debugging. Production code should never cause overflows or require something like this to flag them.

    -Phil

  • cgraceycgracey Posts: 14,151
    edited 2021-03-18 22:46

    There is a smart pin mode which adds a 32-bit settable value into a 32-bit accumulator on every clock and then outputs the unsigned overflow to the pin. That works at the clock rate of the chip. Instead of 750 KHz, you can get 320 MHz. You can also do this in PASM every 8 clocks:

    loop   ADD    a,b    WC    '2
           DRVC   pin          '2
           JMP    #loop        '4
    
  • With the numbers given it overflows in about 22 seconds, which is in fact a bit dismaying. The loop repeats at about 750kHz. I had hoped it would be an order of magnitude faster. By default, is it sharing a cog with the Spin interpreter? If so, would it be faster in its own?

    It has to share the COG with the Spin interpreter, since that's what's running it :).

    The Spin2 interpreter is very efficient, but like any interpreter it has overhad and that's going to particularly show up in math heavy functions. There is a compiler, flexspin, which can compile Spin2 to PASM to run directly. When compiled with flexspin your sample program toggles the LED at a little over once per second (every 1342177 microseconds, according to a DEBUG statement I put in). So it's around 20x faster than the interpreter, which makes sense for a program like this that's very simple. flexspin comes with an IDE called FlexProp, a link to which is in my signature.

  • ScroungreScroungre Posts: 161
    edited 2021-03-20 20:17

    FlexSpin does like 'addpins':
    pinw(46 addpins 9, !(a.[31..22])) ' Fun with my LED array,
    but doesn't seem to like:
    pinw(55..46, !(a.[31..22]))
    ... gets you a syntax error. I haven't paid my syn tax, I guess...

    Thanks, S.

    Edited some more. So far, a fairly happy camper!
    The new loadp2 helped, although with -v it is very unhappy here.

  • (Also thanks to cgracey's suggestions. I've written a lot of AVR assembler, but wasn't too keen on the idea of learning PASM - perhaps I will anyhow!)

  • There's a new flexprop (5.3.0) which should fix the syntax error with pinw, and which has the new loadp2 bundled in.

    What is it unhappy about when -v is given?

Sign In or Register to comment.