3

A function I have takes min, max uint16 parameters and at some point iterates over the numeric range. However, if max happens to be 2^16-1 (and it is a valid use case), then overflow breaks the loop logic. Here is an example code demonstrating the problem with uint8:

package main

import "fmt"

func iter(min, max uint8) {
    for i := min; i <= max; i++ {
        fmt.Printf("%d, ", i)
    }
}

func main() {
    iter(0, 255)
}

As you can see, the program never ends. A similar question was asked at another question but the solution exactly exhibits the same problem I have.

My thinking for now is to convert the loop variable to uint32, similar to this:

package main

import "fmt"

func iter(min, max uint8) {
    for i := uint16(min); i <= uint16(max); i++ {
        fmt.Printf("%d, ", i)
    }
}

func main() {
    iter(0, 255)
}

However, this seems to be a clumsy solution, which is not going to work for uint64 or whatever biggest uintN type. Feels like I am missing something very basic. Guidance?

I am aware of Brad Fitz's Iter solution, but it seems to add unneeded overhead. Is that true as well?

Community
  • 1
  • 1
Vlad Didenko
  • 4,481
  • 4
  • 25
  • 34
  • 1
    I have a hacky solution for you, instead of iterating up to `max` inclusive, iterate up to `max` exclusive. This way an overflow is never reached. – Benjamin Gruenbaum Apr 26 '15 at 16:09
  • I gather this is the style you suggest? https://gist.githubusercontent.com/didenko/ed5144f6349656c683dc/raw/3a4b2d1c565819f54604a97409e6b6b0d0fe50b8/max_special.go – Vlad Didenko Apr 26 '15 at 19:44
  • @VladDidenko: What output do you get for `iter(42, 7)`? https://gist.githubusercontent.com/didenko/ed5144f6349656c683dc/raw/3a4b2d1c565819f54604a97409e6b6b0d0fe50b8/max_special.go – peterSO Apr 26 '15 at 20:31
  • @peterSO argh you are right, more cruft to get rid of the hanging max run. Not worth it. – Vlad Didenko Apr 26 '15 at 20:39
  • 2
    BTW, as mentioned in the answers below, the cleanest (for `uint8` or `uint16` inputs) is to just convert to `uint` (instead of `uint32`!, if you're going to convert you may as well use the native integer size). Especially if you don't need to use the index the as an `uint8` within the loop that will probably also be the fastest. – Dave C Apr 27 '15 at 13:29

1 Answers1

3

For example, for uint8,

package main

import "fmt"

func iter(min, max uint8) {
    {
        min, max := uint(min), uint(max)
        for i := min; i <= max; i++ {
            fmt.Printf("%d, ", i)
        }
    }
}

func main() {
    iter(0, 255)
}

For uint64,

package main

import "fmt"

func iter(min, max uint64) {
    for i := min; i <= max; i++ {
        fmt.Printf("%d, ", i)
        if i == max {
            break
        }
    }
}

func main() {
    iter(^uint64(0)-2, ^uint64(0))
}

Output:

18446744073709551613, 18446744073709551614, 18446744073709551615

Addendum:

Here's my version of Dave C's suggestion.

package main

import "fmt"

func iter(min, max uint64) {
    for i, next := min, min <= max; next; i, next = i+1, i < max {
        fmt.Printf("%#016[1]x ", i)
    }
    fmt.Println()
}

func main() {
    const maxUint64 = ^uint64(0)
    iter(0, 3)
    iter(10, 9)
    iter(maxUint64-2, maxUint64)
}

Output:

0x0000000000000000 0x0000000000000001 0x0000000000000002 0x0000000000000003 
0xfffffffffffffffd 0xfffffffffffffffe 0xffffffffffffffff 
Community
  • 1
  • 1
peterSO
  • 158,998
  • 31
  • 281
  • 276
  • Note in the later case you need to make sure not to use `continue` before the conditional. Also the `for` condition becomes redundant so just `for i := min; ; i++ {` if you like. – Dave C Apr 26 '15 at 18:36
  • Is there a significant difference between the first version in the answer and the one suggested in the question? The second one, after Dave's suggestion may look like this: https://gist.githubusercontent.com/didenko/ed5144f6349656c683dc/raw/e8d14b04700d9a26a4c5140219c811ec4ccb7a5c/check_in_body.go I am concerned about the need to memorize the danger of "continue" before the test, so if a function call is not an issue the a version in the question comments looks somewhat preferable. Do I miss something? – Vlad Didenko Apr 26 '15 at 19:48
  • @DaveC: Whenever you change code you must be sure that you don't introduce bugs. You can do that in many ways. For example, by introducing an errant `continue` or by following your suggestion about removing the `for` statement `i <= max` condition, which will change the behavior of the `iter` function whenever `min > max`. – peterSO Apr 26 '15 at 19:51
  • @peterSO note about the min > max case makes sense. I like special-case of max processing from Benjamin Gruenbaum even more after these considerations. – Vlad Didenko Apr 26 '15 at 19:55
  • @peterSO, gah, you are absolutely right, I completely forgot about `min > max`! I was too focused on not doing two comparisons each loop. If that was a concern, it might make more sense to have an `if min > max {return}` at the start to correctly eliminate the double compare. Your original is cleaner/nicer though. It's a trade off between simplicity and efficiency I think. – Dave C Apr 26 '15 at 20:07
  • 2
    BTW, [here](https://play.golang.org/p/lSJFw2Tje_) is simple (put perhaps hard to read) way to avoid the extra comparisons and yet "do the right thing", including allowing `continue` anywhere after the first line of the loop. – Dave C Apr 27 '15 at 13:34
  • @DaveC: Nice. I would simplify it. See my revised answer. – peterSO Apr 27 '15 at 14:23
  • BTW, if anyone is wondering about that `%…[1]x`, it was a left over from when I was doing something like `Printf("%d = %[1]x", i)`. It should probably be removed now, sorry. (And I only used hex since I the decimal of maxUint64 doesn't mean much to me :). – Dave C Apr 27 '15 at 22:49