News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

CRT printf and LONG LONG variables

Started by jj2007, December 22, 2013, 12:53:53 PM

Previous topic - Next topic

jj2007

Take a loooong number like 12345678901234567890 and try to print it with printf: You'll get 2874452364, which is not exactly the expected result.

Alternatives are MasmBasic's Str$(), or this one:

include \masm32\MasmBasic\MasmBasic.inc                ; download
.data
MyLongLongA        LONGLONG 12345678901234567890        ; 0xAB54A98CEB1F0AD2
MyDouble        DOUBLE ?
MyLongLongB        LONGLONG ?

  Init
  Dll "msvcr100"        ; the standard Masm32 crt lib is not enough
  Declare void printf, C:?        ; don't return anything, C calling convention, vararg
  ; Print "MyLongLongA is ", Hex$(MyLongLongA), Str$(" =  %u\n", MyLongLongA)        ; standard MasmBasic syntax, for comparison
  printf(cfm$("MyLongLongA is %llX aka %llu\n"), MyLongLongA, MyLongLongA)

  fild MyLongLongA        ; these four instructions:
  fstp MyDouble        ; ... convert a LL to a double
  fld MyDouble        ; ... and back, and
  fistp MyLongLongB        ; ... they work like a charm
  printf(cfm$("MyLongLongB is %llX aka %llu\n"), MyLongLongB, MyLongLongB)
  Inkey
  Exit
end start

Output:
MyLongLongA is AB54A98CEB1F0AD2 aka 12345678901234567890
MyLongLongB is AB54A98CEB1F0C00 aka 12345678901234568192


It seems msvrt.dll should be deprecated, dear friends in Redmond :P

By the way, to get this working, I had to spend the whole evening chasing a hilarious bug in ML.exe, versions 6.15 to 10.0. No problem with JWasm, it has become hypercompatible, but MASM... from a famous company in Redmond... :dazzled:

dedndave

 :P
C:\Masm32\Asm32\LLKF9_1 => LlkfTest
-5992310449541053025764350635963415778726589929807688575781002705257755860183365
43775694924408025120070400379599571311192195965896048055408309491435464913747925
89297808288495092100739867082031373151027650695001922946050227447487627157971036
846942412387783503561528766079982415312826572101611785443208074712406

MichaelW

This works as expected:

include \masm32\include\masm32rt.inc
.data
    qw1 dq -9223372036854775808
    qw2 dq 9223372036854775807
.code
start:
    printf("%I64d\n",qw1)
    printf("%I64d\n",qw2)
    printf("%I64Xh\n",qw1)   
    printf("%I64Xh\n\n",qw2)
    inkey
    exit
end start


-9223372036854775808
9223372036854775807
8000000000000000h
7FFFFFFFFFFFFFFFh


Format Specification Syntax

Size Specification

And since your comments seem to imply otherwise, I'll add that only a REAL10 can store the full range of a 64-bit integer.
Well Microsoft, here's another nice mess you've gotten us into.

jj2007

Quote from: MichaelW on December 22, 2013, 06:14:46 PM
This works as expected:

Yes indeed, for your values everything works as expected.

QuoteAnd since your comments seem to imply otherwise, I'll add that only a REAL10 can store the full range of a 64-bit integer.

Sorry, I thought that marking 4 digits in red and blue would be sufficient to indicate that some precision gets lost. Which is different from getting absolute rubbish results...

Attached a testbed, using:
a) MasmBasic Str$() with %u (=unsigned integer, whatever format)
b) Masm32 printf
c) MsVcr100 printf
The latter two are displayed using
1. %llu
2. %I64u

Output: *)

qw1 & qw2, Masm32 printf (using MsVCRT):
-9223372036854775808
9223372036854775807
8000000000000000h
7FFFFFFFFFFFFFFFh

MB:   MyLongLongA is AB54A98C EB1F0AD2 =  12345678901234567890 (MasmBasic)
M32:  MyLongLongA is EB1F0AD2 aka 2874452364 (MsVCRT, llu)
M32:  MyLongLongA is EB1F0AD2 aka -1504471850584594036 (MsVCRT, I64d)
M32:  MyLongLongA is EB1F0AD2 aka 16942272223124957580 (MsVCRT, I64u)
C100: MyLongLongA is AB54A98CEB1F0AD2 aka 12345678901234567890 (msvcr100, llu)
C100: MyLongLongA is AB54A98CEB1F0AD2 aka 12345678901234567890 (msvcr100, I64u)
C100: MyLongLongB is AB54A98CEB1F0C00 aka 12345678901234568192 (msvcr100, 4 digits lost)

qw1 & qw2, MsVcr100 printf:
-9223372036854775808
9223372036854775807
8000000000000000h
7FFFFFFFFFFFFFFFh


*) Note the data declaration for MyLongLongA:
.data
MyLongLongA   LONGLONG 12345678901234567890


One might expect to see 12345678901234567890 ;)

EDIT: Mystery solved - msvcrt.dll has a bad implementation of llX and llu. Test yourself:

include \masm32\include\masm32rt.inc

.code
MyLongLongA LONGLONG 12345678901234567890 ; 0xAB54A98CEB1F0AD2
start:
  print "Expected: AB54A98C EB1F0AD2 aka 12345678901234567890", 13, 10
  printf(cfm$("M32:  MyLongLongA is %llX aka %llu (MsVCRT, llX+llu)\n"), MyLongLongA, MyLongLongA)
  printf(cfm$("M32:  MyLongLongA is %bla aka %bla (MsVCRT, bla+bla)\n"), MyLongLongA, MyLongLongA)
  printf(cfm$("M32:  MyLongLongA is %llX aka %llX (MsVCRT, llX+llX)\n"), MyLongLongA, MyLongLongA)
  inkey " "
  exit 
end start


Expected: AB54A98C EB1F0AD2 aka 12345678901234567890
M32:  MyLongLongA is EB1F0AD2 aka 2874452364 (MsVCRT, llX+llu)
M32:  MyLongLongA is bla aka bla (MsVCRT, bla+bla)
M32:  MyLongLongA is EB1F0AD2 aka AB54A98C (MsVCRT, llX+llX)


I have inserted the bla+bla to show that printf does accept the arguments that it obviously doesn't handle properly; i.e. the invalid bla is just treated as text, while for llX and llu you see (wrong) numbers.

What happens is that, with llX and llu:
- msvcrt.dll erroneously reads DWORD args from stack
- msvcr100.dll correctly reads QWORD args

No errors thrown. The Masm32 PROTOs seem OK, as the stack is balanced. So IMHO it is a fat BUG in the CRT 8)

And googling for "llu" format specifier "msvcrt.dll" reveals that it's a KnownBug in a KnownDLL ;-)