Math error using Macro inline without outer ()'s

Just starting out? Need help? Post your questions and find answers here.
User avatar
skywalk
Addict
Addict
Posts: 3972
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Math error using Macro inline without outer ()'s

Post by skywalk »

This is scary :evil:
Same result for ASM or CBE ±optimizer.

Code: Select all

#small1     = 1e-16
Macro ML_Tburst_from_PW_DC(PW_s_d, ducy_pc_d)
  ; PW_s_d = PulseWidth(s), ducy_pc_d = DutyCycle(%)
  ; Returns Tburst(s) = Burst period for a given Pulse Width and Duty Cycle
  ; Must force Floating point math with leading 0.0+ or /100.0
  ;FYI; inline error if outer parentheses removed!
  (1 / ((1 / Abs(PW_s_d) * (Abs(ducy_pc_d) / 100.0)) + #small1))
EndMacro
Macro ML_Toff_from_PW_DC(PW_s_d, dc_pc_d)
  ; PW_s_d = PulseWidth(s), DC_pc_d = DutyCycle(%)
  ; RETURN: Toff(s) = burst period offtime for a given Pulse Width and Duty Cycle
  ML_Tburst_from_PW_DC(PW_s_d, dc_pc_d) - Abs(PW_s_d)
EndMacro
Debug ML_Tburst_from_PW_DC(20e-6, 2)
Debug 1 / ML_Tburst_from_PW_DC(20e-6, 2)
Debug ML_Toff_from_PW_DC(20e-6, 2)
Debug StrD(ML_Toff_from_PW_DC(20e-6, 2) * 1e6) + " <-- WRONG"
Debug "Should be --> " + StrD(0.00097999999 * 1e6)

X.d = ML_Toff_from_PW_DC(20e-6, 2) * 1e6
Debug StrD(X) + " <-- Should be " + StrD(0.00097999999 * 1e6)
X.d = ML_Toff_from_PW_DC(20e-6, 2)
Debug StrD(X * 1e6) + " <-- OK if broken apart"
Last edited by skywalk on Fri Nov 25, 2022 4:41 pm, edited 2 times in total.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
STARGÅTE
Addict
Addict
Posts: 2067
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: [BUG-v6] Math error with Macro's?

Post by STARGÅTE »

Why you report a bug, if you type a question in the title?

There is a bug in your code. You have to clamp the output of ML_Toff_from_PW_DC()

Code: Select all

#small1     = 1e-16
Macro ML_Tburst_from_PW_DC(PW_s_d, ducy_pc_d)
  ; PW_s_d = PulseWidth(s), ducy_pc_d = DutyCycle(%)
  ; Returns Tburst(s) = Burst period for a given Pulse Width and Duty Cycle
  ; Must force Floating point math with leading 0.0+ or /100.0
  ;FYI; inline error if outer parentheses removed!
  (1 / ((1 / Abs(PW_s_d) * (Abs(ducy_pc_d) / 100.0)) + #small1))
EndMacro
Macro ML_Toff_from_PW_DC(PW_s_d, dc_pc_d)
  ; PW_s_d = PulseWidth(s), DC_pc_d = DutyCycle(%)
  ; RETURN: Toff(s) = burst period offtime for a given Pulse Width and Duty Cycle
  ( ML_Tburst_from_PW_DC(PW_s_d, dc_pc_d) - Abs(PW_s_d) ) ; outer ( ) needed !!!
EndMacro
Debug ML_Tburst_from_PW_DC(20e-6, 2)
Debug 1 / ML_Tburst_from_PW_DC(20e-6, 2)
Debug ML_Toff_from_PW_DC(20e-6, 2)
Debug StrD(ML_Toff_from_PW_DC(20e-6, 2) * 1e6) + " <-- WRONG"
Debug "Should be --> " + StrD(0.00097999999 * 1e6)

X.d = ML_Toff_from_PW_DC(20e-6, 2) * 1e6
Debug StrD(X) + " <-- Should be " + StrD(0.00097999999 * 1e6)
X.d = ML_Toff_from_PW_DC(20e-6, 2)
Debug StrD(X * 1e6) + " <-- OK if broken apart"
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
skywalk
Addict
Addict
Posts: 3972
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: [BUG-v6] Math error with Macro's?

Post by skywalk »

Thank you!
I forgot to read my own notes.
--> ;FYI; inline error if outer parentheses removed!

But I still consider this a bug.

Code: Select all

;https://www.purebasic.fr/english/viewtopic.php?t=80175
#small1     = 1e-16
Macro ML_Tburst_from_PW_DC(PW_s_d, ducy_pc_d)
  ; PW_s_d = PulseWidth(s), ducy_pc_d = DutyCycle(%)
  ; Returns Tburst(s) = Burst period for a given Pulse Width and Duty Cycle
  ; Must force Floating point math with leading 0.0+ or /100.0
  ;FYI; inline error if outer parentheses removed!
  (1 / ((1 / Abs(PW_s_d) * (Abs(ducy_pc_d) / 100.0)) + #small1))
EndMacro
Macro ML_Toff_from_PW_DC(PW_s_d, dc_pc_d)
  ; PW_s_d = PulseWidth(s), DC_pc_d = DutyCycle(%)
  ; RETURN: Toff(s) = burst period offtime for a given Pulse Width and Duty Cycle
  ;FYI; inline error if outer parentheses removed!
  (ML_Tburst_from_PW_DC(PW_s_d, dc_pc_d) - Abs(PW_s_d))
EndMacro
Debug ML_Tburst_from_PW_DC(20e-6, 2)
Debug 1 / ML_Tburst_from_PW_DC(20e-6, 2)
Debug ML_Toff_from_PW_DC(20e-6, 2)
Debug StrD(ML_Toff_from_PW_DC(20e-6, 2) * 1e6) + " <-- WRONG"
Debug "Should be --> " + StrD(0.00097999999 * 1e6)

X.d = ML_Toff_from_PW_DC(20e-6, 2) * 1e6
Debug StrD(X) + " <-- Should be " + StrD(0.00097999999 * 1e6)
X.d = ML_Toff_from_PW_DC(20e-6, 2)
Debug StrD(X * 1e6) + " <-- OK if broken apart "

; HERE IS THE MACRO BROKEN OUT WITH AND WITHOUT THE OUTSIDE ()'s.
Debug ((1 / ((1 / Abs(20e-6) * (Abs(2) / 100.0)) + #small1)) - Abs(20e-6))
Debug (1 / ((1 / Abs(20e-6) * (Abs(2) / 100.0)) + #small1)) - Abs(20e-6)
; THEY BOTH WORK, but not from the MACRO.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
jacdelad
Addict
Addict
Posts: 1431
Joined: Wed Feb 03, 2021 12:46 pm
Location: Planet Riesa
Contact:

Re: [BUG-v6] Math error with Macro's

Post by jacdelad »

I don't get it, the macro replaces the places where it's put with its content, so in your code line 17 becomes

Code: Select all

Debug StrD(ML_Tburst_from_PW_DC(PW_s_d, dc_pc_d) - Abs(PW_s_d) * 1e6) + " <-- WRONG"
while you expect it to become

Code: Select all

Debug StrD((ML_Tburst_from_PW_DC(PW_s_d, dc_pc_d) - Abs(PW_s_d)) * 1e6) + " <-- WRONG"
...which it would if you put a parenthesis around the content of the macro.
This isn't a bug.
PureBasic 6.04/XProfan X4a/Embarcadero RAD Studio 11/Perl 5.2/Python 3.10
Windows 11/Ryzen 5800X/32GB RAM/Radeon 7770 OC/3TB SSD/11TB HDD
Synology DS1821+/36GB RAM/130TB
Synology DS920+/20GB RAM/54TB
Synology DS916+ii/8GB RAM/12TB
juergenkulow
Enthusiast
Enthusiast
Posts: 544
Joined: Wed Sep 25, 2019 10:18 am

Re: [BUG-v6] Math error with Macro's

Post by juergenkulow »

Wolframalpha 979.99999999999999990000000000000000000999 (period 38)
We could use more than 53-bit precision of double values for this calculation.

Code: Select all

; 0.0010000000475
; 1000.0
; 0.0009800000000000001851991
; 980 <-- WRONG
; Should be --> 979.99999
; 980 <-- Should be 979.99999
; 980 <-- OK If broken apart 
; 0.0009800000000000001851991
; 0.0009800000000000001851991

Code: Select all

; only 53 bit Precision in double
#small1     = 1e-16
s.s=StrD(((1 / ((1 / Abs(20e-6) * (Abs(2) / 100.0)) + #small1)) - Abs(20e-6)) * 1e6) + " <-- WRONG"+#CRLF$
;       123    45       6     6   6   7 7        65          43      3     32      1 
Debug s
x.d=20e-6
Debug x
Debug Abs(20e-6)
Debug (1 / Abs(20e-6) * (Abs(2) / 100.0))
Debug #small1
Debug (1 / Abs(20e-6) * (Abs(2) / 100.0))+#small1
Debug (1 / ((1 / Abs(20e-6) * (Abs(2) / 100.0)) + #small1))
Debug ((1 / ((1 / Abs(20e-6) * (Abs(2) / 100.0)) + #small1)) - Abs(20e-6))
Debug 0.0009800000000000001 *1e6

; double rr0=PB_Abs_DOUBLE(2.000000000000000163606107828062619091724627651274204254150390625e-05);
; double rr1=PB_Abs_DOUBLE(2.0);
; double rr2=PB_Abs_DOUBLE(2.000000000000000163606107828062619091724627651274204254150390625e-05);
; double p0=(double)((double)(((double)((1.0/(((((double)(1.0/rr0)*(((double)rr1/100.0))))+9.999999999999999790977867240346035618411149408467364363417573258630000054836273193359375e-17))))-rr2))*1000000.0);

; 980 <-- WRONG
; 
; 0.0000200000000000000016361
; 0.0000200000000000000016361
; 999.9999999999998863131622784
; 0.0000000000000001
; 999.9999999999998863131622784
; 0.0010000000475
; 0.0009800000000000001851991
; 980.0
Please ask your questions, because switch on the cognition apparatus decides on the only known life in the universe.Wersten :DDüsseldorf NRW Germany Europe Earth Solar System Flake Bubble Orionarm
Milky Way Local_Group Virgo Supercluster Laniakea Universe
User avatar
skywalk
Addict
Addict
Posts: 3972
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: [BUG-v6] Math error with Macro's

Post by skywalk »

jacdelad wrote: Thu Nov 24, 2022 5:43 am I don't get it, the macro replaces the places where it's put with its content, so in your code line 17 becomes

Code: Select all

Debug StrD(ML_Tburst_from_PW_DC(PW_s_d, dc_pc_d) - Abs(PW_s_d) * 1e6) + " <-- WRONG"
while you expect it to become

Code: Select all

Debug StrD((ML_Tburst_from_PW_DC(PW_s_d, dc_pc_d) - Abs(PW_s_d)) * 1e6) + " <-- WRONG"
...which it would if you put a parenthesis around the content of the macro.
This isn't a bug.
Why does my manual equation work then?
I guess the documentation should state outer()'s required for numerical operations. We cannot do the same with string macro.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
jacdelad
Addict
Addict
Posts: 1431
Joined: Wed Feb 03, 2021 12:46 pm
Location: Planet Riesa
Contact:

Re: [BUG-v6] Math error with Macro's

Post by jacdelad »

Ah, now I see what you mean. Yeah, that's strange...
PureBasic 6.04/XProfan X4a/Embarcadero RAD Studio 11/Perl 5.2/Python 3.10
Windows 11/Ryzen 5800X/32GB RAM/Radeon 7770 OC/3TB SSD/11TB HDD
Synology DS1821+/36GB RAM/130TB
Synology DS920+/20GB RAM/54TB
Synology DS916+ii/8GB RAM/12TB
User avatar
STARGÅTE
Addict
Addict
Posts: 2067
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: [BUG-v6] Math error with Macro's

Post by STARGÅTE »

I do not see what you mean.
skywalk wrote: Thu Nov 24, 2022 3:31 pm Why does my manual equation work then?
In your manual equation you do no multiplication * 1e6 after the subtraction.
skywalk wrote: Thu Nov 24, 2022 3:31 pm I guess the documentation should state outer()'s required for numerical operations. We cannot do the same with string macro.
Putting ()'s around operations has nothing to do with Macros. It is simple math, when you want to calculate a sum (or difference) before product.

I guess sometimes, there is still the opinion macros are just "faster" or "quicker" procedures. But this is completely wrong!
Macros are compiler replacements and they have no return value and no input parameter. They are just placeholders and they have just placeholders as arguments!

If you define a macro like:

Code: Select all

Macro Subtract(a, b)
	a - b
EndMacro
it will be replace one-by-one in any cases:

Code: Select all

Debug Subtract(8, 2) * 3      ;-->   8 - 2 * 3 = 2
Debug Subtract(8, 3+2)        ;-->   8 - 3 + 2 = 7
But I guess, you think the results should be 18 and 3, like it is in case of a procedure, where the procedure and its arguments are evaluated before the rest:

Code: Select all

Procedure Subtract(a, b)
	ProcedureReturn a - b
EndProcedure

Debug Subtract(8, 2) * 3      ;-->   8 - 2 = 6   -->  6 * 3 = 18
Debug Subtract(8, 3+2)        ;-->   3 + 2 = 5   -->  8 - 5 = 3
But then you have to use parenthesis:

Code: Select all

Debug (8 - 2) * 3
Debug 8 - (3+2)
And therefore parenthesis in the macro or outside:

Code: Select all

Macro Subtract(a, b)
	( (a) - (b) )
EndMacro

Debug Subtract(8, 2) * 3
Debug Subtract(8, 3+2)
And again, in your case, there is no advantage to use a macro instead of a regular procedure, especially if you keep the following in mind:
In you second macro:

Code: Select all

ML_Tburst_from_PW_DC(PW_s_d, dc_pc_d) - Abs(PW_s_d)
the placeholder "PW_s_d" is used twice and therefore is also calculated twice! So you lose evaluation time and generate trouble.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
skywalk
Addict
Addict
Posts: 3972
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: [BUG-v6] Math error with Macro's

Post by skywalk »

Thanks for the thorough reply Stargate.
I looked through all my math macros and they do all have enclosed ()'s.
I goofed on this last one and was fooled by the manual entry giving me the correct answer. I will review tomorrow when the tryptophan wears off.
Happy Thanksgiving!
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
skywalk
Addict
Addict
Posts: 3972
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: [BUG-v6] Math error with Macro's

Post by skywalk »

Please add a numerical Macro example to the Help doc. (Macro Help only details string Macro's.)
The surrounding ()'s issue tricked me me several times now. :oops:
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
STARGÅTE
Addict
Addict
Posts: 2067
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Math error using Macro inline without outer ()'s

Post by STARGÅTE »

I think there is no "numerical Macro" in the documentation, because it is no typical usage of a macro.
They could probably add a "Possible Issues" section to highlight the special case of compiler replacements in combination with math operations.

However, why you didn't use procedures?

Code: Select all

#small1     = 1e-16
Procedure.d ML_Tburst_from_PW_DC(PW_s.d, ducy_pc.d)
  ; PW_s_d = PulseWidth(s), ducy_pc_d = DutyCycle(%)
  ; Returns Tburst(s) = Burst period for a given Pulse Width and Duty Cycle
	ProcedureReturn 1 / ((1 / Abs(PW_s) * (Abs(ducy_pc) / 100.0)) + #small1)
EndProcedure
Procedure.d ML_Toff_from_PW_DC(PW_s.d, dc_pc.d)
  ; PW_s_d = PulseWidth(s), DC_pc_d = DutyCycle(%)
  ; RETURN: Toff(s) = burst period offtime for a given Pulse Width and Duty Cycle
  ProcedureReturn ML_Tburst_from_PW_DC(PW_s, dc_pc) - Abs(PW_s)
EndProcedure
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
skywalk
Addict
Addict
Posts: 3972
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Math error using Macro inline without outer ()'s

Post by skywalk »

Way back when converting to PB, I found noticeable time penalties with Procedure() instead of Macro().
So long ago, I forgot about the darn ()'s.
I did not want stuff like this in separate Procedure calls.

Code: Select all

Macro ML_PtXYinRECT(pPT, pRECT) ; Macros can accept Structures unlike Procedures needing *pt.Point, *r.RECT references
  ((pPT\x > pRECT\left) And (pPT\x < pRECT\right) And (pPT\y > pRECT\top) And (pPT\y < pRECT\bottom))
EndMacro
Macro ML_XYinRECT(x, y, pRECT) ; Macros can accept Structures unlike Procedures needing *r.RECT reference
  ((x > pRECT\left) And (x < pRECT\right) And (y > pRECT\top) And (y < pRECT\bottom))
EndMacro
Notice I used outer ()'s. :idea:
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
BarryG
Addict
Addict
Posts: 3292
Joined: Thu Apr 18, 2019 8:17 am

Re: [BUG-v6] Math error with Macro's

Post by BarryG »

STARGÅTE wrote: Thu Nov 24, 2022 7:58 pmthere is still the opinion macros are just "faster" or "quicker" procedures. But this is completely wrong!
Well, Fred did say there was overhead and a speed penalty with Procedures -> viewtopic.php?p=31871#p31871

A macro is just code replacement at compile time, so they will always be faster than a procedure because procedures have the added overhead of runtime initialisation and cleanup during use (like prepping and clearing the stack). For example:

Code: Select all

Macro MacroTest(arg)
  arg*2
EndMacro

Procedure ProcedureTest(arg)
  ProcedureReturn arg*2
EndProcedure

DisableDebugger
n=1
start.q=ElapsedMilliseconds()
For c=1 To 10000000
  n=MacroTest(n)
Next
stop.q=ElapsedMilliseconds()-start
EnableDebugger
Debug stop ; 25 ms for Macro

DisableDebugger
n=1
start.q=ElapsedMilliseconds()
For c=1 To 10000000
  n=ProcedureTest(n)
Next
stop.q=ElapsedMilliseconds()-start
EnableDebugger
Debug stop ; 1600 ms for Procedure
Last edited by BarryG on Sat Nov 26, 2022 5:31 am, edited 1 time in total.
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: [BUG-v6] Math error with Macro's

Post by Little John »

BarryG wrote: Sat Nov 26, 2022 2:37 am
STARGÅTE wrote: Thu Nov 24, 2022 7:58 pmthere is still the opinion macros are just "faster" or "quicker" procedures. But this is completely wrong!
A macro is just code replacement at compile time, so they will always be faster than a procedure because procedures have the added overhead of runtime initialisation and cleanup during use (like prepping and clearing the stack). For example:

Code: Select all

Macro MacroTest(arg)
  arg*2
EndMacro

Procedure ProcedureTest(arg)
  ProcedureReturn arg*2
EndProcedure

DisableDebugger
n=1
start.q=ElapsedMilliseconds()
For c=1 To 10000000
  n=MacroTest(n)
Next
stop.q=ElapsedMilliseconds()-start
EnableDebugger
Debug stop ; 25 ms for Macro

DisableDebugger
n=1
start.q=ElapsedMilliseconds()
For c=1 To 10000000
  n=ProcedureTest(n)
Next
stop.q=ElapsedMilliseconds()-start
EnableDebugger
Debug stop ; 1600 ms for Procedure
For realistic time measurement, switch the debugger completely off in the IDE.

Code: Select all

EnableExplicit

CompilerIf #PB_Compiler_Debugger
   CompilerError "Switch the debugger *off* in the IDE"
CompilerEndIf


Macro MacroTest(arg)
   arg * 2
EndMacro

Procedure ProcedureTest(arg)
   ProcedureReturn arg * 2
EndProcedure


Define n.i, c.i, t1.q, t2.q

n = 1
t1 = ElapsedMilliseconds()
For c = 1 To 100000000
   n = MacroTest(n)
Next
t1 = ElapsedMilliseconds() - t1

n = 1
t2 = ElapsedMilliseconds()
For c = 1 To 100000000
   n = ProcedureTest(n)
Next
t2 = ElapsedMilliseconds() - t2

MessageRequester("Result", Str(t1) + " ms with Macro," + #LF$ +  ; 317 ms
                           Str(t2) + " ms with Procedure")       ; 291 ms
BarryG
Addict
Addict
Posts: 3292
Joined: Thu Apr 18, 2019 8:17 am

Re: [BUG-v6] Math error with Macro's

Post by BarryG »

Little John wrote: Sat Nov 26, 2022 4:57 amFor realistic time measurement, switch the debugger completely off in the IDE.

Code: Select all

EnableExplicit

CompilerIf #PB_Compiler_Debugger
   CompilerError "Switch the debugger *off* in the IDE"
CompilerEndIf


Macro MacroTest(arg)
   arg * 2
EndMacro

Procedure ProcedureTest(arg)
   ProcedureReturn arg * 2
EndProcedure


Define n.i, c.i, t1.q, t2.q

n = 1
t1 = ElapsedMilliseconds()
For c = 1 To 100000000
   n = MacroTest(n)
Next
t1 = ElapsedMilliseconds() - t1

n = 1
t2 = ElapsedMilliseconds()
For c = 1 To 100000000
   n = ProcedureTest(n)
Next
t2 = ElapsedMilliseconds() - t2

MessageRequester("Result", Str(t1) + " ms with Macro," + #LF$ +  ; 317 ms
                           Str(t2) + " ms with Procedure")       ; 291 ms
Okay. Did that, ran your code, and got this:

Image

Procedures are slower for me. Have no idea why they're faster for you.
Last edited by BarryG on Sat Nov 26, 2022 5:16 am, edited 2 times in total.
Post Reply