Open GWRon opened 2 years ago
This code could be useful to classify a string literal integer to initially classify if it can fit within an Int, UInt, Long or ULong. There are some prerequisites and assumptions to the string data in that it expects 1 minus 'sign' digit at most at the beginning of the string, and expects the rest of the string to contain digits.
Credit where it is due... I ported the algorithm from the MS c++ stl library. The original algorithm works with multiple bases and I've limited this port to just base 10 for BlitzMax.
SuperStrict
Type IntegerLiteralTypeClassification
Method New()
UMaxInt = UInt(-1)
MaxInt = UMaxInt Shr 1
ABSMinInt = MaxInt + 1
UMaxLong = ULong(-1)
MaxLong = UMaxLong Shr 1
ABSMinLong = MaxLong + 1
EndMethod
Method ClassifyInt:Int(In:String)
Local StartIndex:Int = 0
If In[0] = Asc("-")
RiskyInt = AbsMinInt / 10
MaxDigitInt = AbsMinInt Mod 10
StartIndex = 1
Else
RiskyInt = MaxInt / 10
MaxDigitInt = MaxInt Mod 10
EndIf
Return ClassifyInteger(In, StartIndex)
EndMethod
Method ClassifyUInt:Int(In:String)
RiskyInt = UMaxInt / 10
MaxDigitInt = UMaxInt Mod 10
Return ClassifyInteger(In, 0)
EndMethod
Method ClassifyLong:Int(In:String)
Local StartIndex:Int = 0
If In[0] = Asc("-")
RiskyLong = AbsMinLong / 10
MaxDigitLong = AbsMinLong Mod 10
StartIndex = 1
Else
RiskyLong = MaxLong / 10
MaxDigitLong = MaxLong Mod 10
EndIf
Return ClassifyLongInteger(In, StartIndex)
EndMethod
Method ClassifyULong:Int(In:String)
RiskyLong = UMaxLong / 10
MaxDigitLong = UMaxLong Mod 10
Return ClassifyLongInteger(In, 0)
EndMethod
Private
Method ClassifyInteger:Int(In:String, StartIndex:Int)
Local Value:UInt = 0
Local Overflowed:Int = False
For Local i:Int = StartIndex Until In.Length
Local Digit:Int = In[i] - 48
If Value < RiskyInt Or (Value = RiskyInt And Digit <= MaxDigitInt)
Value = Value * 10 + Digit
Else
Overflowed = True
EndIf
Next
If Overflowed = True
Return False
EndIf
Return True
EndMethod
Method ClassifyLongInteger:Int(In:String, StartIndex:Int)
Local Value:ULong = 0
Local Overflowed:Int = False
For Local i:Int = StartIndex Until In.Length
Local Digit:Int = In[i] - 48
If Value < RiskyLong Or (Value = RiskyLong And Digit <= MaxDigitLong)
Value = Value * 10 + Digit
Else
Overflowed = True
EndIf
Next
If Overflowed = True
Return False
EndIf
Return True
EndMethod
Field UMaxInt:UInt
Field MaxInt:UInt
Field AbsMinInt:UInt
Field RiskyInt:UInt
Field MaxDigitInt:UInt
Field UMaxLong:ULong
Field MaxLong:ULong
Field AbsMinLong:ULong
Field RiskyLong:ULong
Field MaxDigitLong:ULong
EndType
Local Classifier:IntegerLiteralTypeClassification = New IntegerLiteralTypeClassification
Print Classifier.ClassifyInt("-2147483648") ' fits into Int
Print Classifier.ClassifyInt("-2147483649") ' does not fit into Int
Print Classifier.ClassifyInt("2147483648") ' does not fit into Int
Print Classifier.ClassifyUInt("2147483648") ' fits into UInt
Print Classifier.ClassifyLong("-9223372036854775808") ' fits into Long
Print Classifier.ClassifyLong("9223372036854775807") ' fits into Long
Print Classifier.ClassifyLong("9223372036854775808") ' does not fit into Long
Print Classifier.ClassifyULong("9223372036854775808") ' fits into ULong
Print Classifier.ClassifyULong("18446744073709551615") ' fits into ULong
Print Classifier.ClassifyULong("18446744073709551616") ' does not fit into ULong
Assume you have a function accepting "long" and you want to manually pass a value.
Output is:
BCC should possibly detect the "size/detail" of a passed value - and then decide if to really default to int/float, or if a long/double would fit better. Or at least print out a warning, that the second call (the one without ":long") leads to potential data loss (as it is understood as an "int" here)