Bit #63 (1st MSB) can bring some errors.
a = $F0 << 56
b = $07 << 56
Normal (signed) compare :
a < b
Unsigned compare :
a > b
So, a new IfUnsigned or UnsignedIf should be required to allow the coder to choose.
It is just a op change : JB instead of JL on ASM.
IfUnsigned a < b
Re: IfUnsigned a < b
better yet just add unsigned types. It's not difficult!
Re: IfUnsigned a < b
Hello idle,
I have seen your message. And good link :
viewtopic.php?t=65312.
Note that this feature request here is just a small internal change. And this is not the add of a new type unsigned.
More detailed, this would modify the four condition ops :
If a < b
If a =< b
If a > b
If a >= b
And this could only concern the bold conditions above. So, really not a lot.
I have seen your message. And good link :
viewtopic.php?t=65312.
Note that this feature request here is just a small internal change. And this is not the add of a new type unsigned.
More detailed, this would modify the four condition ops :
If a < b
If a =< b
If a > b
If a >= b
And this could only concern the bold conditions above. So, really not a lot.
Re: IfUnsigned a < b
Wouldn't it be better to use different operator than a new If? This IfUnsigned limits you to one value, a new operator enables multiple comparations at once. And signed an not signed mixed together.
Good morning, that's a nice tnetennba!
PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
Re: IfUnsigned a < b
So, yes and no.jacdelad wrote: Mon Oct 09, 2023 10:15 am Wouldn't it be better to use different operator than a new If? This IfUnsigned limits you to one value, a new operator enables multiple comparations at once. And signed an not signed mixed together.
Yes, it limits. And this pushes to add parenthesis.
Code: Select all
IfUnsigned(condition)
So, it is limited in the program, but in these limits, any values, and any conditions can be tested, but ever as unsigned.
It is a few like floating operations between integers, or integer operations between floats : the mode is limited and determined by the type of destination on the statement start.
The main problem is, it seems more complex in C where a temporary destination should be required.
Re: IfUnsigned a < b
This could work, but I don't see why not just add unsigned types. This feels like a bandaid at best, and if they implement unsigned types (PLEASE DO!) they'll probably have to rip it out and deprecate old code.
Re: IfUnsigned a < b
Adding unsigned types isn't hard to do in the assembly and it's dead to do in c and it wouldn't break anything.
Fortunately It's easy enough to redefine a type as unsigned in the c backend for example
Fortunately It's easy enough to redefine a type as unsigned in the c backend for example
Code: Select all
Global x.l,y.l
!unsigned int g_x;
!unsigned int g_y;
x = -1
y = 1
If x > y ;if x is unsigend its 4294967295
Debug x
Else ;if x is signed it's -1
Debug x
EndIf
Re: IfUnsigned a < b
As much as I'd love unsigned support, Fred and Timo seemed to pretty strongly reject it in the past:
For example, given:
Which of the following is printed?
In C, the comparison is effectively "If 1 > 18446744073709551615" and so following that logic we'd see "1 is not greater than -1" logged (which is a really silly erroneous statement). In this case, the programmer seemingly forgot a StrU(…).
If PB were to handle the comparison by first ensuring y is a non-negative signed value, this lowers performance, but has the more sensible "1 is greater than -1" logged. IMO, comparing the actual numeric values represented in this way is more natural than intricacies of bits behind-the-scenes. So arguably this might be the more BASIC route.
Alternatively, would the comparison just be forbidden entirely?
My dream would be to have unsigned added with safety checks to prevent ambiguous mixing and allow opt-in to either comparison form:
Though, that's adding casts and a world of potential complexity, which probably can't be called "BASIC" and is a slight departure from the way existing type conversions (transparently) work.
Still, it'd be super useful in the handful of cases where unsigned types are desired, and is possibly the safest and clearest way it might be done. Because mixing signed and unsigned is generally rare, explicit satisfaction of safety measures would also be rarely necessary, but when they do kick-in they'd help avoid serious bugs.
(In my very humble opinion)
((Thank you for coming to my TED talk
))
https://www.purebasic.fr/english/viewtopic.php?p=295680#p295680 wrote: Fri Aug 14, 2009 3:06 pm These are primary for use to manipulate characters (ascii or unicode) indepently of the main 'Unicode' flag (unlike the .c type). Indeed, it works perfectly as well for unsigned byte and word. That said we don't plan to add further unsigned types.
From an implementation standpoint, it might not be too bothersome, though Timo's point about it being unintuitive is sound.https://www.purebasic.fr/english/viewtopic.php?p=296495#p296495 wrote: Tue Aug 18, 2009 11:03 pm We don't see much value in adding unsigned long or quad especially since all PB commands expect signed long or quad numbers as input. An unsigned byte or word can be cast to a signed long and passed to all these functions without problems, but passing an unsigned long to a function that expects a signed one is trouble. So what good is an unsigned type when you can pass it to almost no function to work with the value ?
Also as Fred explains in the link by luis, mixing signed and unsigned is very tricky in the implementation/performance department. For example to compare an unsigned byte to a signed long, you just cast the unsigned byte to a long and then compare (the same that needs to be done to compare signed byte and signed long). To compare an unsigned long with a signed one is a different story. The cases where the signed one is negative or the unsigned one is over the range of a signed long have to be handled separately because a direct comparison between both is not possible. Casting both up to quad just for a comparison is also not very fast on the x86 processor family as its over the native register size.
In the end, the number of situations where there is a real need for unsigned longs is quite low in my opinion (except to interface with external libraries maybe). So in our opinion it is just not worth the hassle.
For example, given:
Code: Select all
Define x.ui = 1 ; .ui → unsigned int (.iu or .i-unsigned might be alternatives?)
Define y.i = -1
If x > y
Debug "" + x + " is greater than " + y
Else
Debug "" + x + " is not greater than " + y
EndIf
- "1 is greater than -1"
- "1 is not greater than -1"
- nothing: it's a compiler error
In C, the comparison is effectively "If 1 > 18446744073709551615" and so following that logic we'd see "1 is not greater than -1" logged (which is a really silly erroneous statement). In this case, the programmer seemingly forgot a StrU(…).
If PB were to handle the comparison by first ensuring y is a non-negative signed value, this lowers performance, but has the more sensible "1 is greater than -1" logged. IMO, comparing the actual numeric values represented in this way is more natural than intricacies of bits behind-the-scenes. So arguably this might be the more BASIC route.
Alternatively, would the comparison just be forbidden entirely?
My dream would be to have unsigned added with safety checks to prevent ambiguous mixing and allow opt-in to either comparison form:
Code: Select all
; 1) Writing signed values to unsigned is an error.
; ═══════════════════════════════════════════════════════
Define x.ul = -1 ; ERR: Signed value `-1` cannot be assigned to unsigned long `x.ul`. Either assign to `x.ul\signed` to perform the necessary cast, or supply a value in range 0 .. 4_294_967_295.
Define y.l = 1
Define x.ul = y ; ERR: Signed `y.l` cannot be assigned to unsigned long `x.ul`. Assign to `x.ul\signed` to perform the necessary cast.
; 2) Writing unsigned values to signed is an error.
; ═══════════════════════════════════════════════════════
Define y.l = 4294967295 ; OK: backwards-compatibility.
Define x.ul = 1
Define y.l = x ; ERR: Unsigned `x.ul` cannot be assigned to signed long `y.l`. Assign to `y.l\unsigned` to perform the necessary cast.
; 3) Comparisons are signedness-aware.
; ═══════════════════════════════════════════════════════
Define x.ul = 1
Define y.l = -1
; ERR: Comparing unsigned `x.ul` against signed `y.l` is ambiguous.
; Specify the type of comparison with either `x\signed > y` or `x > y\unsigned`.
; Alternatively, opt-in to mixed-signedness comparison by clarifying both sides, e.g.: `x\unsigned > y\signed`.
If x > y
Debug "" + x + " is greater than " + y
Else
Debug "" + x + " is not greater than " + y
EndIf
; OK: Comparison is treated as signed.
; Becomes: If 1 > -1
; Prints: "1 is greater than -1"
If x\signed > y
Debug "" + x + " is greater than " + y
Else
Debug "" + x + " is not greater than " + y
EndIf
; OK: Comparison is treated as unsigned.
; Becomes: If 1 > 18446744073709551615
; Prints: "1 is not greater than 18446744073709551615"
If x\unsigned > y
Debug "" + x + " is greater than " + StrU(y)
Else
Debug "" + x + " is not greater than " + StrU(y)
EndIf
; OK: Comparison is mixed-signedness-aware.
; Becomes: If (-1 < 0) Or (1 > 18446744073709551615)
; Prints: "1 is greater than -1"
If x\unsigned > y\signed
Debug "" + x + " is greater than " + y
Else
Debug "" + x + " is not greater than " + y
EndIf
; OK: No need to specify signedness when LHS and RHS are same signedness.
; This means the vast majority of cases are transparently handed.
Define z.ul = x
If x = z
EndIf
Still, it'd be super useful in the handful of cases where unsigned types are desired, and is possibly the safest and clearest way it might be done. Because mixing signed and unsigned is generally rare, explicit satisfaction of safety measures would also be rarely necessary, but when they do kick-in they'd help avoid serious bugs.
(In my very humble opinion)
((Thank you for coming to my TED talk

Re: IfUnsigned a < b
I would suggest using an unsigned compare procedure which returns -1, 0 or 1 depending on the result of the comparison.
Code: Select all
ProcedureC UL_CMP(a.l, b.l)
CompilerIf Defined(PB_Backend_C, #PB_Constant) And #PB_Compiler_Backend = #PB_Backend_C
!return ((unsigned long)v_a > (unsigned long)v_b) - ((unsigned long)v_a < (unsigned long)v_b);
CompilerElse
!xor eax, eax
!xor edx, edx
!mov ecx, [p.v_a]
!cmp ecx, [p.v_b]
!seta al
!setb dl
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!sub rax, rdx
CompilerElse
!sub eax, edx
CompilerEndIf
ProcedureReturn
CompilerEndIf
EndProcedure
ProcedureC UL_CMP_PTR(*a.Long, *b.Long)
CompilerIf Defined(PB_Backend_C, #PB_Constant) And #PB_Compiler_Backend = #PB_Backend_C
!return (*(unsigned long*)p_a > *(unsigned long*)p_b) - (*(unsigned long*)p_a < *(unsigned long*)p_b);
CompilerElse
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rax, [p.p_a]
!mov rdx, [p.p_b]
!mov ecx, [rax]
!cmp ecx, [rdx]
!seta al
!setb dl
!movzx eax, al
!movzx edx, dl
!sub rax, rdx
CompilerElse
!mov eax, [p.p_a]
!mov edx, [p.p_b]
!mov ecx, [eax]
!cmp ecx, [edx]
!seta al
!setb dl
!movzx eax, al
!movzx edx, dl
!sub eax, edx
CompilerEndIf
ProcedureReturn
CompilerEndIf
EndProcedure
ProcedureC UQ_CMP(a.q, b.q)
CompilerIf Defined(PB_Backend_C, #PB_Constant) And #PB_Compiler_Backend = #PB_Backend_C
!return ((unsigned long long)v_a > (unsigned long long)v_b) - ((unsigned long long)v_a < (unsigned long long)v_b);
CompilerElse
!xor eax, eax
!xor edx, edx
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rcx, [p.v_a]
!cmp rcx, [p.v_b]
!seta al
!setb dl
!sub rax, rdx
CompilerElse
!mov ecx, [p.v_a+4]
!cmp ecx, [p.v_b+4]
!jne .cnt
!mov ecx, [p.v_a]
!cmp ecx, [p.v_b]
!.cnt:
!seta al
!setb dl
!sub eax, edx
CompilerEndIf
ProcedureReturn
CompilerEndIf
EndProcedure
ProcedureC UQ_CMP_PTR(*a.Quad, *b.Quad)
CompilerIf Defined(PB_Backend_C, #PB_Constant) And #PB_Compiler_Backend = #PB_Backend_C
!return (*(unsigned long long*)p_a > *(unsigned long long*)p_b) - (*(unsigned long long*)p_a < *(unsigned long long*)p_b);
CompilerElse
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rax, [p.p_a]
!mov rdx, [p.p_b]
!mov rcx, [rax]
!cmp rcx, [rdx]
!seta al
!setb dl
!movzx eax, al
!movzx edx, dl
!sub rax, rdx
CompilerElse
!mov eax, [p.p_a]
!mov edx, [p.p_b]
!mov ecx, [eax+4]
!cmp ecx, [edx+4]
!jne .cnt
!mov ecx, [eax]
!cmp ecx, [edx]
!.cnt:
!seta al
!setb dl
!movzx eax, al
!movzx edx, dl
!sub eax, edx
CompilerEndIf
ProcedureReturn
CompilerEndIf
EndProcedure
Dim n.l(4)
n(0) = $90000000
n(1) = $40000000
n(2) = $12344321
n(3) = $12341234
n(4) = $fffffffe
Debug "signed compare"
If n(0) < n(1)
Debug "$"+Hex(n(0), #PB_Long) + " < $" + Hex(n(1), #PB_Long)
Else
Debug "$"+Hex(n(0), #PB_Long) + " >= $" + Hex(n(1), #PB_Long)
EndIf
Debug ""
Debug "unsigned compare"
If UL_CMP(n(0), n(1)) < 0
Debug "$"+Hex(n(0), #PB_Long) + " < $" + Hex(n(1), #PB_Long)
Else
Debug "$"+Hex(n(0), #PB_Long) + " >= $" + Hex(n(1), #PB_Long)
EndIf
Debug ""
ImportC ""
qsort(*base, num, size, *comparator)
EndImport
Debug "unsigned sorting"
qsort(@n(), ArraySize(n())+1, SizeOf(Long), @UL_CMP_PTR())
For i = 0 To ArraySize(n())
Debug "$"+Hex(n(i), #PB_Long)
Next
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)