Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Overflow and wrapping

Integer arithmetic wraps when a result does not fit its type: one past the largest int32 is the smallest, and nothing reports it. std/numeric lets a program say what should happen instead.

#import "std/numeric"

dim as int32 top = 2147483647

'' Stored back in an int32, a sum past the top wraps, silently.
dim as int32 wrapped = top + 1
print "top + 1 as int32 ="; wrapped

'' The same happens to unsigned types, at both ends.
dim as uint8 small = 255
small += 1
print "255 + 1 as uint8 = "; small
small -= 1
print "0 - 1 as uint8   = "; small

'' Checked: an empty Optional instead of a wrong number.
var sum = numeric.AddChecked( top, 1 )
print "AddChecked( top, 1 ) has a value: "; sum.HasValue( )
print "AddChecked( top, -1 ) ="; numeric.AddChecked( top, -1 ).Value( )

'' Saturating: stop at the end of the range.
print "AddSaturating( top, 1 ) ="; numeric.AddSaturating( top, 1 )

'' Wrapping: what + does, spelled so a reader knows it was meant.
print "AddWrapping( top, 1 )   ="; numeric.AddWrapping( top, 1 )

'' ^ works in float64, so above 2^53 an integer power is rounded.
dim as int64 b = 3, e = 38
dim as int64 viaPow = b ^ e
print "3 ^ 38       ="; viaPow
print "IPow( 3, 38 ) ="; numeric.IPow( b, e )

Output

top + 1 as int32 =-2147483648
255 + 1 as uint8 = 0
0 - 1 as uint8   = 255
AddChecked( top, 1 ) has a value: false
AddChecked( top, -1 ) = 2147483646
AddSaturating( top, 1 ) = 2147483647
AddWrapping( top, 1 )   =-2147483648
3 ^ 38       = 1350851717672992000
IPow( 3, 38 ) = 1350851717672992089

Unsigned types wrap at both ends: a uint8 holding 255 plus one is 0, and 0 minus one is 255.

FamilyOn overflow
AddChecked, SubChecked, MulChecked, DivChecked, NegChecked, AbsChecked, IPowCheckedreturn an empty Optional
AddSaturating and its siblingsstop at the end of the range
AddWrapping and its siblingswrap, exactly as the operators do

The wrapping family exists so that deliberate wrapping, in a hash or a checksum, says it is deliberate. A checked result is an Optional: HasValue( ) says whether there is one and Value( ) reads it; see Error Handling.

Pitfall

^ on integers is not exact above 2^53. It converts both sides to float64, and a float64 holds 53 bits of precision, so 3 ^ 38 comes back rounded, by 89. Use numeric.IPow for integer powers.

These functions protect the calls you thought to protect; overflow elsewhere is still silent.

Activities

  1. Use MulChecked to find the first factorial that does not fit in an int32.
  2. Give two uint8 variables the values 0 and 1, and compare numeric.SubSaturating of them with their difference stored in a uint8.