Skip to content

Overflow condition of Range(Int, Int)#bsearch #13923

Open
@HertzDevil

Description

@HertzDevil

The midpoint between two endpoints in Range#bsearch is computed as from + ((to - from) >> 1). This overflows if both from and to are the same signed integer type:

(-100_i8..100_i8).bsearch { true }        # Arithmetic overflow (OverflowError)
(Int32::MIN..Int32::MAX).bsearch { true } # Arithmetic overflow (OverflowError)

as 200 and 0xFFFFFFFF do not fit into an Int8 or an Int32 respectively. These calls of course should never raise, because the midpoint of two integers of the same type is guaranteed to fit into that type's range (after truncation).

The situation is more complicated if the two endpoints' types are different:

(Int8::MIN..UInt8::MAX).bsearch(&.>= -100) # -100?
(Int8::MIN..UInt8::MAX).bsearch(&.>= 200)  # 200?

If these values were Int32, the first call would yield 63 then -33, and the second call would yield 63 then 159. If we fix the yielded value's type to either Int8 or UInt8, then an overflow would always happen. We should decide a sensible behavior here:

  • Since any two integer types will overlap in range, any two values' midpoint will also fit into at least one type's range. Always try to find that type. The block must therefore accept a B | E. (This would also apply to Fix float number overflow in range rand method #13825, actually.)
  • Specify that only values fitting into B's range are allowed; if any midpoint exceeds B's range, raise OverflowError even if a valid first element within B's range exists. The block then accepts a B, similar to Indexable#bsearch.

Extra union types in B and E should not matter; (1_i8.as(Int8 | Int32)..10_u8.as(UInt8 | UInt32)).bsearch will only ever yield Int8 | UInt8s, never an Int32 nor UInt32.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions