Page 4 of 5

Re: Format string with some values like sprintf (C) - Update

Posted: Thu Jul 19, 2018 12:55 pm
by luis
wilbert wrote: Probably the x64 calling convention.
But it does the same on x86

Code: Select all

ImportC ""
 swprintf(*str.string,format.s,*args1=0,*args2=0)
EndImport

Global str.s{128}  
Global i = 12
Global f.f = 3.14

;   FLD    dword [v_f]
;   PUSH   eax
;   FISTP  dword [esp]
;   PUSH   dword [v_i]
;   MOV    eax,_S1
;   PUSH   eax
;   PUSH   dword v_str
;   CALL  _swprintf

swprintf(@str,"test %i %f",i, f) ; test 12 0.000000

Debug str
If I'm not mistaken it just pushes the two 32 bits quantities on the stack, so shouldn't %f interpret the 32 bit data as a float ?

Re: Format string with some values like sprintf (C) - Update

Posted: Thu Jul 19, 2018 1:55 pm
by wilbert
luis wrote:If I'm not mistaken it just pushes the two 32 bits quantities on the stack, so shouldn't %f interpret the 32 bit data as a float ?
FISTP stores the float as an integer which won't work.
But there also seems to be a bug on PureBasic Windows x64.

I can't get swprintf to work cross platform so here's an example with sprintf instead.

Code: Select all

ImportC ""
  sprintf(*str, format.p-ascii, arg1.d=0, arg2.d=0)
EndImport

Global Dim buf.a(128)

sprintf(@buf(), "test  %f", 3.14)
Debug PeekS(@buf(), -1, #PB_Ascii)

sprintf(@buf(), "test  %f  %f", 3.14, Sqr(2))
Debug PeekS(@buf(), -1, #PB_Ascii)
This code should work cross platform but doesn't on Windows x64.
On a 64 bit platform, the floating point arguments need to be passed by XMM0 and XMM1 but it looks like PB Windows x64 uses the wrong XMM registers :shock:

Re: Format string with some values like sprintf (C) - Update

Posted: Thu Jul 19, 2018 2:33 pm
by luis
wilbert wrote: FISTP stores the float as an integer which won't work.
Woops, thanks. I misunderstood the difference between FSTP and FISTP. That explain why we can't just use all integers in the prototype and make it work for floats/doubles too.

But why even specifying float instead of integer in the prototype doesn't work ?

Code: Select all

; Win x86

ImportC ""
  sprintf_with_float (*str.string,format.s,args1, f.f) As "_swprintf" ; does not work using the right type ?
  sprintf_with_double (*str.string,format.s,args1, f.d) As "_swprintf" ; work using double ?
EndImport

Global str.s{128}  
Global i = 12
Global f.f = 3.14 ; this is a float

sprintf_with_float (@str,"test %i %f",i, f) ; test 12 0.000000 ; ZERO ?
Debug str
sprintf_with_double (@str,"test %i %f",i, f) ; test 12 3.140000 
Debug str
sprintf_with_double (@str,"test %i %lf",i, f) ; test 12 3.140000
Debug str

Re: Format string with some values like sprintf (C) - Update

Posted: Thu Jul 19, 2018 2:59 pm
by wilbert
luis wrote:But why even specifying float instead of integer in the prototype doesn't work ?
From what I've read online when it comes to C,
float arguments to variadic function are promoted to double.
So for a function like sprintf / swprintf, you always need to use a double when importing the function or using PrototypeC.
After that, passing a float (.f) works fine as PB will convert it to a double.

Re: Format string with some values like sprintf (C) - Update

Posted: Thu Jul 19, 2018 3:07 pm
by luis
float arguments to variadic function are promoted to double.
Oh, didn't know that, thanks very much Wilbert, now at least I've got an explanation for that.

Re: Format string with some values like sprintf (C) - Update

Posted: Thu Jul 19, 2018 3:08 pm
by pthien
wilbert wrote:
luis wrote:But why even specifying float instead of integer in the prototype doesn't work ?
From what I've read online when it comes to C,
float arguments to variadic function are promoted to double.
So for a function like sprintf / swprintf, you always need to use a double when importing the function or using PrototypeC.
After that, passing a float (.f) works fine as PB will convert it to a double.
I've used this code, obviously not with 64-bit code handling floating-point types, though.

I just wonder if someone us going to post fixed code when the discussion of what is wrong is concluded and a universal solution identified?

:D

Re: Format string with some values like sprintf (C) - Update

Posted: Thu Jul 19, 2018 3:14 pm
by skywalk

Code: Select all

ImportC ""
  _snprintf(*sRes.p-Ascii, nBytes.i, sFmt.p-Ascii, sTxt.p-Ascii, NumToFmt.d)
EndImport
Global.i nBytes = #MAX_PATH
Global sRes.s = Space(nBytes)
_snprintf(@sRes, nBytes, "%s %6.6e", "Result = ", -#PI*2000e6)
Debug PeekS(@sRes, -1, #PB_Ascii)
[/url]This function call works on PB v562 x64.

Re: Format string with some values like sprintf (C) - Update

Posted: Thu Jul 19, 2018 3:29 pm
by wilbert
skywalk wrote:This function call works on PB v562 x64.
You are right but I still believe there's a bug as this doesn't work

Code: Select all

ImportC ""
  _snprintf(*sRes.p-Ascii, nBytes.i, sFmt.p-Ascii, NumToFmt.d)
EndImport
Global.i nBytes = #MAX_PATH
Global sRes.s = Space(nBytes)
_snprintf(@sRes, nBytes, "%6.6e", -#PI*2000e6)
Debug PeekS(@sRes, -1, #PB_Ascii)

Re: Format string with some values like sprintf (C) - Update

Posted: Thu Jul 19, 2018 5:30 pm
by skywalk
Yes, the bug is confusing :evil:

Code: Select all

ImportC ""
  _snprintf(*sRes.p-Ascii, nBytes.i, sFmt.p-Ascii, sTxt.p-Ascii, a2.d)
  _snprintf1(*sRes.p-Ascii, nBytes.i, sFmt.p-Ascii, a2.d) As "_snprintf"
EndImport
Global.i nBytes = #MAX_PATH
Global sRes.s = Space(nBytes)
_snprintf(@sRes, nBytes, "%s %6.6e", "Result = ", -#PI*2000e6)
Debug PeekS(@sRes, -1, #PB_Ascii)
_snprintf1(@sRes, nBytes, "%6.6e", -#PI*2000e6)
Debug PeekS(@sRes, -1, #PB_Ascii)

Re: Format string with some values like sprintf (C) - Update

Posted: Thu Jul 19, 2018 8:34 pm
by mk-soft
First:
C-LIB not support float, only double
Second:

Please open a new topic for C-LIB printf... -> Link: viewtopic.php?f=13&t=71083

Thanks :wink:

Re: Format string with some values like sprintf (C) - Update

Posted: Wed Jul 19, 2023 1:35 pm
by AZJIO
Can leading zeros be added?

Code: Select all

EnableExplicit
XIncludeFile "Format.pbi"
Define y = 2023, m = 7, d = 5, h = 8, i = 45, s = 9
Debug Format("%04i-%02i-%02i", @y, @m, @d) 
Debug Format("%02i:%02i:%02i", @h, @i, @s) 
Debug Format("%04i-%02i-%02i %02i:%02i:%02i", @y, @m, @d, @h, @i, @s)
I found how to do it. It would be nice to have examples in the first post.

Code: Select all

Define y = 2008, m = 4, d = 1, h = 8, i = 45, s = 9
Debug Format("%04i-%'02i-%'02i", @y, @m, @d) 
Debug Format("%'02i:%'02i:%'02i", @h, @i, @s) 
Debug Format("%'04i-%'02i-%'02i %'02i:%'02i:%'02i", @y, @m, @d, @h, @i, @s) 

Re: Format string with some values like sprintf (C) - Update

Posted: Wed Jul 19, 2023 2:40 pm
by pthien
I added leading zeros by defining "pad" as a space (default), and then changing pad to "0" should the width parameter be preceded by a zero.

My code is probably terrible I lifted this from this very thread and modified it, but here it is:

Code: Select all

Macro __Format__(text, max, pad)
   If max
      If max < 0
         max = ~max
         max + 1
         If Len(text) <= max
            text = LSet(text, max, pad)
         EndIf
      Else
         If Len(text) <= max
            text = RSet(text, max, pad)
         EndIf
      EndIf
   EndIf
EndMacro

Procedure.s Format(text.s, *value1 = 0, *value2 = 0, *value3 = 0, *value4 = 0, *value5 = 0, *value6 = 0, *value7 = 0, *value8 = 0, *value9 = 0, *value10 = 0, *value11 = 0)
   
   Protected zeiger, *value, pos1, pos2, len1, len2, exit
   Protected type, result.s, help.s, Format.s
   Protected is_op = #False
   Protected pad.s = " "
   
   zeiger = @*value1
   result = text
   
   result = ReplaceString(result, "\r", #CR$)
   result = ReplaceString(result, "\n", #LF$)
   result = ReplaceString(result, "\t", #TAB$)
   result = ReplaceString(result, "\\", "\")
   
   Repeat
      pos1 = FindString ( result, "%", pos1 )
      If pos1
         *value = PeekL ( zeiger )
         pos2 = pos1 + 1
         exit = #False
         Repeat
            type = Asc ( Mid ( result, pos2, 1 ) )
            
            is_op = #False
            Select type
               Case 98, 119, 108, 113, 102, 100, 115 ; "b", "w", "l", "q", "f", "d", "s"
                  Format = Mid ( result, pos1 + 1, pos2 - pos1 - 1 )
                  len1   = Val ( StringField ( Format, 1, "." ) )
                  len2   = Val ( StringField ( Format, 2, "." ) )
                  is_op  = #True
            EndSelect
            
            Select type
               Case 98
                  help = Str  ( PeekB ( *value ) )       ; "b"
               Case 119
                  help = Str  ( PeekW ( *value ) )       ; "w"
               Case 108
                  help = Str  ( PeekL ( *value ) )       ; "l"
               Case 113
                  help = Str  ( PeekQ ( *value ) )       ; "q" (StrQ depricated so changed to Str)
               Case 102
                  help = StrF ( PeekF ( *value ), len2 ) ; "f"
               Case 100
                  help = StrD ( PeekD ( *value ), len2 ) ; "d"
               Case 115
                  help =        PeekS ( *value )         ; "s"
               Case 37
                  help = "%"
                  result = Left ( result, pos1 - 1 ) + Mid ( result, pos2, Len(result)) ; "%"
               Case 43, 45, 46                                                          ; "-" "+" "."
                  pos2 + 1
               Case 49 To 57 ; "1" "2" "3" "4" "5" "6" "7" "8" "9"
                  pos2 + 1
               Case 48       ; "0"
                  pos2 + 1
                  pad = "0"
               Default
                  exit = #True
            EndSelect
            
            If is_op = #True
               __Format__( help, len1, pad )
               result = Left ( result, pos1 - 1 ) + help + Mid ( result, pos2 + 1, Len(result))
               zeiger + 4
               exit = #True
            EndIf
            
         Until exit
         pos1 + Len ( help )
      Else
         Break
      EndIf
   ForEver
   
   ProcedureReturn result
   
EndProcedure

Re: Format string with some values like sprintf (C) - Update

Posted: Wed Jul 19, 2023 4:49 pm
by AZJIO

Code: Select all

Define d.d = 123.5e-20
Debug Format("%d", @d)
Returns 0.

Re: Format string with some values like sprintf (C) - Update

Posted: Wed Jul 19, 2023 5:01 pm
by pthien
AZJIO wrote: Wed Jul 19, 2023 4:49 pm

Code: Select all

Define d.d = 123.5e-20
Debug Format("%d", @d)
Returns 0.
Looks like you're gonna have to dig in.

Are the earlier examples in this thread working properly with doubles? It should be easy to change the padding character if so.

Re: Format string with some values like sprintf (C) - Update

Posted: Wed Jul 19, 2023 7:13 pm
by ebs
AZJIO wrote: Wed Jul 19, 2023 4:49 pm

Code: Select all

Define d.d = 123.5e-20
Debug Format("%d", @d)
Returns 0.
I assume that you have to specify the number of decimal places for floating point numbers:

Code: Select all

Define v.d = 123.5e-20
Debug Format("%1.22d", @v)
results in '0.0000000000000000012350'