Float/Double to String with Locale Language Format

Just starting out? Need help? Post your questions and find answers here.
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Float/Double to String with Locale Language Format

Post by mk-soft »

I need this for all OS

Code: Select all

;-TOP

; *****************************************************************************

Procedure.s LocaleStrF(fltVal.f)
  Protected result.s
  
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      Protected value.variant
      value\vt = #VT_R4
      value\fltVal = fltVal
      If VariantChangeType_(value, value, 0, #VT_BSTR) = #S_OK
        result = PeekS(value\bstrVal, - 1, #PB_Unicode)
        VariantClear_(value)
      EndIf
      
    CompilerCase #PB_OS_MacOS
      ;TODO
      
    CompilerCase #PB_OS_Linux
      ;TODO
      
  CompilerEndSelect
  
  ProcedureReturn result
    
EndProcedure

; -----------------------------------------------------------------------------

Procedure.s LocaleStrD(dblVal.d)
  Protected result.s
  
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      Protected value.variant
      value\vt = #VT_R8
      value\dblVal = dblVal
      If VariantChangeType_(value, value, 0, #VT_BSTR) = #S_OK
        result = PeekS(value\bstrVal, - 1, #PB_Unicode)
        VariantClear_(value)
      EndIf
      
    CompilerCase #PB_OS_MacOS
      ;TODO
      
    CompilerCase #PB_OS_Linux
      ;TODO
      
  CompilerEndSelect
  
  ProcedureReturn result
    
EndProcedure

; *****************************************************************************

;- Test

CompilerIf #PB_Compiler_IsMainFile
  
  Define fVal.f, dVal.d
  
  Debug "Locale format of Float"
  fVal = 1234.567
  Debug LocaleStrF(fVal)
  PokeL(@fVal, $FFFFFFFF)
  Debug LocaleStrF(fVal)
  
  Debug "Locale format of Double"
  dVal = 123456.56789
  Debug LocaleStrD(dVal)
  PokeQ(@dVal, $FFFFFFFFFFFFFFFF)
  Debug LocaleStrD(dVal)
  
CompilerEndIf
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
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: Float/Double to String with Locale Language Format

Post by Trond »

For 64-bit linux:

Code: Select all


#LC_ALL = 6
Import ""
  setlocale(a, *b)
EndImport
ImportC ""
  snprintf(*result.Ascii, result_size.i, format.p-ascii, number.d)
EndImport

; Just for checking that the solution doesn't modify the stack, remove after verifying
!mov [v_rsp], rsp
Debug rsp
rsp.i

; Save old locale
*oldlocale = setlocale(#LC_ALL, 0)

; Set to default locale
setlocale(#LC_ALL, "");
#result_size = 50

*result = AllocateMemory(#result_size)
;snprintf(*result, #result_size, "%.15f", 123.456)
snprintf(*result, #result_size, "%f", 123.456)
result.s = PeekS(*result, -1, #PB_Ascii)
FreeMemory(*result)

; Restore old locale
setlocale(#LC_ALL, *oldlocale)

Debug result

; Check that stack is balanced, should be equal to previous. Use esp on 32-bit
!mov [v_rsp], rsp
Debug rsp

For the format parameter, see here for how to use it:
http://www.cplusplus.com/reference/cstdio/printf/

Warning: setlocale() could potentially change the behaviour of other parts of the program
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Float/Double to String with Locale Language Format

Post by mk-soft »

Is not optimize. Please testing...

Update

Code: Select all

;-TOP

; *****************************************************************************

CompilerIf #PB_Compiler_OS = #PB_OS_Linux
  #LC_ALL = 6
  Import ""
    setlocale(a, *b)
  EndImport
  ImportC ""
    snprintf(*result.Ascii, result_size.i, format.p-ascii, number.d)
  EndImport
CompilerEndIf

Procedure.s LocaleStrF(fltVal.f)
  Protected result.s
  
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      Protected value.variant
      value\vt = #VT_R4
      value\fltVal = fltVal
      If VariantChangeType_(value, value, 0, #VT_BSTR) = #S_OK
        result = PeekS(value\bstrVal, - 1, #PB_Unicode)
        VariantClear_(value)
      EndIf
      
    CompilerCase #PB_OS_MacOS
      Protected value, formatter, string
      value = CocoaMessage(0, 0, "NSNumber numberWithFloat:@", @fltVal)
      formatter = CocoaMessage(0, 0, "NSNumberFormatter new")
      CocoaMessage(0, formatter, "setLocale:", CocoaMessage(0, 0, "NSLocale currentLocale"))
      CocoaMessage(0, formatter, "setNumberStyle:", 1); 1 = decimals; 4 = Scientific style
      string = CocoaMessage(0, formatter, "stringFromNumber:", value)
      CocoaMessage(0, formatter, "release")
      result = PeekS(CocoaMessage(0, string, "UTF8String"), -1, #PB_UTF8)
      
    CompilerCase #PB_OS_Linux
      Protected string.s{22}, *oldlocale
      *oldlocale = setlocale(#LC_ALL, 0)
      setlocale(#LC_ALL, "");
      snprintf(@string, 20, "%f", fltVal)
      setlocale(#LC_ALL, *oldlocale);
      result.s = PeekS(@string, -1, #PB_Ascii)
      
  CompilerEndSelect
  
  ProcedureReturn result
    
EndProcedure

; -----------------------------------------------------------------------------

Procedure.s LocaleStrD(dblVal.d)
  Protected result.s
  
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      Protected value.variant
      value\vt = #VT_R8
      value\dblVal = dblVal
      If VariantChangeType_(value, value, 0, #VT_BSTR) = #S_OK
        result = PeekS(value\bstrVal, - 1, #PB_Unicode)
        VariantClear_(value)
      EndIf
      
    CompilerCase #PB_OS_MacOS
      Protected value, formatter, string
      value = CocoaMessage(0, 0, "NSNumber numberWithDouble:@", @dblVal)
      formatter = CocoaMessage(0, 0, "NSNumberFormatter new")
      CocoaMessage(0, formatter, "setNumberStyle:", 1); 1 = Standard; 4 = Scientific style
      string = CocoaMessage(0, formatter, "stringFromNumber:", value)
      CocoaMessage(0, formatter, "release")
      result = PeekS(CocoaMessage(0, string, "UTF8String"), -1, #PB_UTF8)

    CompilerCase #PB_OS_Linux
      Protected string.s{22}, *oldlocale
      *oldlocale = setlocale(#LC_ALL, 0)
      setlocale(#LC_ALL, "");
      snprintf(@string, 20, "%f", dblVal)
      setlocale(#LC_ALL, *oldlocale);
      result.s = PeekS(@string, -1, #PB_Ascii)
      
  CompilerEndSelect
  
  ProcedureReturn result
    
EndProcedure

; *****************************************************************************

;- Test

CompilerIf #PB_Compiler_IsMainFile
  
  Define fVal.f, dVal.d
  
  Debug "Locale format of Float"
  fVal = 1234.567
  Debug LocaleStrF(fVal)
  PokeL(@fVal, $FFFFFFFF)
  Debug LocaleStrF(fVal)
  
  Debug "Locale format of Double"
  dVal = 123456.56789
  Debug LocaleStrD(dVal)
  PokeQ(@dVal, $FFFFFFFFFFFFFFFF)
  Debug LocaleStrD(dVal)
  
CompilerEndIf
Last edited by mk-soft on Sun Jun 24, 2018 4:00 pm, edited 1 time in total.
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
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Re: Float/Double to String with Locale Language Format

Post by Trond »

Why do you comment out setlocale(#LC_ALL, "");? You risk using the "C" locale instead of the default locale, in case some library sets the locale to something different (or PB does it).
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Float/Double to String with Locale Language Format

Post by mk-soft »

Ok...

change code...

A first problem is the conversion from float to double.
If we get another result.
The function snprintf works as X64 always as double.
But if the program should run as X86, there is a problem with double values
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
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Float/Double to String with Locale Language Format

Post by mk-soft »

Now code optimize... :wink:

Code: Select all

;-TOP

; *****************************************************************************

CompilerIf #PB_Compiler_OS = #PB_OS_Linux
  #LC_ALL = 6
  Import ""
    setlocale(Type, *name)
    localeconv()
  EndImport
  
  ImportC ""
    snprintf(*result.Ascii, result_size.i, format.p-ascii, number.d)
  EndImport

  Structure lconv ; {
     *decimal_point;
     *thousands_sep;
     *grouping;	
     *int_curr_symbol;
     *currency_symbol;
     *mon_decimal_point;
     *mon_thousands_sep;
     *mon_grouping;
     *positive_sign;
     *negative_sign;
     int_frac_digits.a;
     frac_digits.a;
     p_cs_precedes.a;
     p_sep_by_space.a;
     n_cs_precedes.a;
     n_sep_by_space.a;
     p_sign_posn.a;
     n_sign_posn.a;
   EndStructure   ;} lconv
CompilerEndIf

; *****************************************************************************

Procedure.s LocaleDecimals()
  Protected result.s
  
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      Protected r1.s{2}
      If GetLocaleInfo_(#LOCALE_USER_DEFAULT, #LOCALE_SDECIMAL, @r1, 2)
        result = r1
      Else
        result = "."
      EndIf
      
    CompilerCase #PB_OS_MacOS
      Protected locale
      locale = CocoaMessage(0, 0, "NSLocale currentLocale", 0)
      result.s = PeekS(CocoaMessage(0, CocoaMessage(0, locale, "decimalSeparator", 0), "UTF8String"), -1, #PB_UTF8)

    CompilerCase #PB_OS_Linux
      Protected *info.lconv, *locale
      *locale = setlocale(#LC_ALL, 0)
      setlocale(#LC_ALL, #Empty$)
      *info = localeconv()
      setlocale(#LC_ALL, *locale)
      result = PeekS(*info\decimal_point , 1, #PB_Ascii)
      
  CompilerEndSelect
  
  ProcedureReturn result
    
EndProcedure

; *****************************************************************************

Global LocalDecimals.s = LocaleDecimals()

Procedure.s LocaleStrF(fltVal.f, decimals=-1)
  If decimals >= 0
    ProcedureReturn ReplaceString(StrF(fltVal, decimals), ".", LocalDecimals)
  Else
    ProcedureReturn ReplaceString(StrF(fltVal), ".", LocalDecimals)
  EndIf  
EndProcedure

; -----------------------------------------------------------------------------

Procedure.s LocaleStrD(dblVal.d, decimals=-1)
  If decimals >= 0
    ProcedureReturn ReplaceString(StrD(dblVal, decimals), ".", LocalDecimals)
  Else
    ProcedureReturn ReplaceString(StrD(dblVal), ".", LocalDecimals)
  EndIf  
EndProcedure

; *****************************************************************************

;- Test

CompilerIf #PB_Compiler_IsMainFile
  
  Define fVal.f, dVal.d
  
  Debug "Locale format of Float"
  fVal = 1.2345
  Debug LocaleStrF(fVal, 4)
  PokeL(@fVal, $FFFFFFFF)
  Debug LocaleStrF(fVal)
  
  Debug "Locale format of Double"
  dVal = 123456.123456
  Debug LocaleStrD(dVal)
  PokeQ(@dVal, $FFFFFFFFFFFFFFFF)
  Debug LocaleStrD(dVal)
  
CompilerEndIf
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
Post Reply