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

Share your advanced PureBasic knowledge/code with the community.
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

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

Post 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 ?
"Have you tried turning it off and on again ?"
A little PureBasic review
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

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

Post 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:
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

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

Post 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
"Have you tried turning it off and on again ?"
A little PureBasic review
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

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

Post 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.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

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

Post 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.
"Have you tried turning it off and on again ?"
A little PureBasic review
pthien
Enthusiast
Enthusiast
Posts: 145
Joined: Sun Jun 29, 2003 9:39 pm

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

Post 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
User avatar
skywalk
Addict
Addict
Posts: 3999
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

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

Post 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.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

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

Post 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)
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
skywalk
Addict
Addict
Posts: 3999
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

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

Post 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)
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
mk-soft
Always Here
Always Here
Posts: 5405
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

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

Post 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:
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
AZJIO
Addict
Addict
Posts: 1360
Joined: Sun May 14, 2017 1:48 am

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

Post 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) 
pthien
Enthusiast
Enthusiast
Posts: 145
Joined: Sun Jun 29, 2003 9:39 pm

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

Post 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
AZJIO
Addict
Addict
Posts: 1360
Joined: Sun May 14, 2017 1:48 am

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

Post by AZJIO »

Code: Select all

Define d.d = 123.5e-20
Debug Format("%d", @d)
Returns 0.
pthien
Enthusiast
Enthusiast
Posts: 145
Joined: Sun Jun 29, 2003 9:39 pm

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

Post 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.
ebs
Enthusiast
Enthusiast
Posts: 530
Joined: Fri Apr 25, 2003 11:08 pm

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

Post 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'
Post Reply