Hi,
[Edited, oops, missed initialising the var in the below code. There is a second post from me below that has the correct code, with the problem replicated.]
Currently doing a front end (DOS) for a little utility for making data files of a given size with repeating data (current main project is extending a duplicate finder, base already works and using that, to find duplicates based on content not name, duplicates get moved out to a designated folder). I have about 20 main library files, file work, string, stringlist, etc. Currently updating my string number unit, had a function in there whereby the user could enter a number in string form, optionally with commas, and the function removed the commas and returned the actual value (generally do Boolean functions and return the var in either Var or Out param). Like most of my number functions, this used DWord.
Decided to update this function, thought it would be useful to extend the range to include negative and QWord (though wouldn't be needed for the front end, I do like to be thorough when I can, and I would have a general number converter). Removing the commas was extremely easy and took only 1 line using StringReplace. You know you have those moments as a programmer (and probably in life) where you find yourself thinking "Why oh Why did I start this?" First problem was getting around pascal's very tight type checking (dear old $V is gone, though not sure how useful it would have been anyway). Eventually worked out a way with untyped reference param and pointer. Got weird results, realized it was a register issue (fixed that). Since I call the converter with just the var and there is no way to know anything about the Var (SizeOf on the untyped reference returns 0 for some reason, can't use Array of Const, I do use that quite often, because there is a 'bug' and it doesn't take DWord, get around by either converting to a string or cast to QWord, but no use here).
Finally got the bugs out of code. Had to replace StrToInt, it had too many issues and too many variations (not tried old reliable Val yet, but if that has gone from the old Turbo Pascal style to the Free Pascal style, then suspect I will be out of luck). So wrote my own string to number converter, easy enough (though really should be unnecessary). Running auto testing (the one good aspect of getting old, is what I have lost in speed of coding I have gained in commenting and testing). The unsigned number types all worked fine (had to reduce the QWord test, it was taking way too long to test EVERY number, used large steps until near the end then went back to single step).
This is the function header, just to make it a bit clearer:
Function p_str_num__str_to_num( Const s_in : String; Var n_ret; size_of_var : Byte; signed_var : Boolean = False ) : Boolean;
p_str_num__ is my own prefix, all my units have their own prefix, so I know it's mine and which unit it is in at a glance.
s_in is the string to convert, this gets tested, so if any errors, exits with False.
n_ret is the var where the number is returned to, this can be any type.
size_of_var, because the function has no way of knowing how big the var is, whilst not ideal, it gets around that problem (e.g. for a Word type, pass 2, for LongInt, pass 4).
signed_var, normally with unsigned, so only need to add True for signed.
Then started testing the signed, positive first. Int8 fine, Int16, not fine. Got an error when it reached 256, i.e. called the convert function with "256" and byte size 2. After some tracking down, found the culprit and to say I was baffled is putting it mildly. I extracted the core essence of the problem, just to make sure I hadn't done something stupid (99% of the time I think there is a bug in pascal, find that I've done something stupid).
[Removed the invalid test extract, the code in the post below is correct and shows the problem.]
Just for completeness, here is the section of the actual test code:
Var
i : QWord;
i16 : Int16 = 0; // signed Word - weird, this solved the 0 problem, it seems this value was not correct going in, but this is never used, would use Out but had issues
s, s2 : ShortString;
// just the relevant vars
// i16
WriteLn( 'i16 +' );
// [note I wanted the string number to be independent of the index (in the large types had to change that due to huge step, sadly pascal doesn't include Step in For loop)]
s := '0';
For i := 0 To 32767 Do Begin // [hard coded in the max values, normally prefer hex, but decimal looked better here]
If Not p_str_num__str_to_num( s, i16, 2, p_str_num__has_sign ) Then Begin
WriteLn; WriteLn( '| ERROR CONVERTING: s = ', s, ', i = ', i );
Exit;
End;
s2 := i16.ToString; // [tested ToString over a range of values and types and found it to be perfect]
If s <> s2 Then Begin
WriteLn; WriteLn( '| ERROR: converted number does not match' );
WriteLn( ' s = ', s, ', i = ', i, ', i16 = ', i16 );
Exit;
End;
If Not p_str_num__add_1_to_str_num( s ) Then Begin // this is my own function to add 1 to any string number, no size limit apart from ShortString
WriteLn( '| ERROR: problem with add 1' );
Exit;
End;
End;
Was reading about the whole RHS business with Free pascal after running into QWord problems (nice solution by Thaddy to locally turn off range checking). But this has got me stumped.
So the core question is does anyone have experience of doing mixed type arithmetic where they have found a simple way around pascal jumping the gun and presuming the type or going to the lowest type?
Luckily it wasn't crucial, just means either ditching any idea of working with signed numbers (was toying with idea going the whole way and write the core in assembler, already have quite a bit of assembler code in my pascal, but first it would reduce the hope of making the code portable and more importantly, I'd either have to limit the program to 64 bit machines, or exclude 8 byte types, or try and emulate pascal great 32 bit get around with the Hi Lo record.
[Added: Really sorry, totally forgot my manners. Thank you for any help offered. Phil,]
Program Details:
Lazarus: 3.4 (date: 2024-05-25) FPC: 3.2.2 Version: Win64
Operating system: Windows Pro 10, 22H2
Hardware: Intel Core i5-3570K CPU, 3.40GHz, RAM: 8GB, 64 bit
Bio (give a take a year or 2):
1980s: commercial programmer in small companies, self taught, including assembler (main language dBASEIII)
1990s: Computing degree, introduced to Turbo Pascal, loved it, final year project: my own programming language
Took a long break from computing
2018: returned to hobby programming, found Free Pascal and Lazarus (hooray, didn't have to use C++)
Write mostly file management programs, done a few programs for friends (may one day put stuff on a website)