首页
社区
课程
招聘
[求助]masm里面。怎么将32位浮点数。转换成字串
发表于: 2011-2-15 16:35 6909

[求助]masm里面。怎么将32位浮点数。转换成字串

2011-2-15 16:35
6909
【求助】masm里面。怎么将32位浮点数。转换成字串

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (11)
雪    币: 656
活跃值: (448)
能力值: ( LV12,RANK:360 )
在线值:
发帖
回帖
粉丝
2
.386 
            .model   flat,   stdcall 
            option   casemap   :none       
;##############################################################
            include windows.inc 
            include user32.inc 
            include kernel32.inc 
            includelib kernel32.lib 
            includelib user32.lib 
;##############################################################
            _sprintf typedef PROTO   C   :DWORD,:VARARG 
            sprintf typedef ptr   _sprintf 
;##############################################################
.const 
        dllname db     'msvcrt.dll ',0 
        fucname db     'sprintf',0 
        output  db     'my   name   is   %s,age   is   %lf',0 
        szname      db     'hopy ',0 
        age       dq     123.11;8个字节即是double类型,有效位为15~16
        tip      db    'can not find msvcrt.dll',0
;##############################################################
.data? 
        fucaddr sprintf ? 
        buf db 256 dup(?) 
;##############################################################
.code 
    start: 
        invoke LoadLibrary,addr    dllname 
        .if    !eax
            invoke    MessageBox,0,addr tip,0,0
        .else
            invoke GetProcAddress,eax,addr    fucname 
            mov fucaddr,eax 
            invoke fucaddr,addr    buf,addr    output,addr    szname,age
            invoke MessageBox,NULL,addr   buf,0,MB_OK 
        .endif
            invoke ExitProcess,NULL 
end   start

你运气好啊.我找了很久才解决的!
2011-2-15 16:50
0
雪    币: 25
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
sprinf可以这样调用。真不错。我把库加在里面却调用不了
2011-2-15 17:15
0
雪    币: 25
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
但是我的的一个dword,这个函数要的是一个Qdword
2011-2-15 17:16
0
雪    币: 656
活跃值: (448)
能力值: ( LV12,RANK:360 )
在线值:
发帖
回帖
粉丝
5
%lf 改成%f  Qdword 改成dword 就行了
2011-2-15 17:17
0
雪    币: 25
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
谢谢。先试下
2011-2-15 17:21
0
雪    币: 359
活跃值: (41)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
虽然对masm不了解,但根据个人对C/C++以及反汇编的了解,对5L的做法能起作用持怀疑态度,大致有两处疑点。
希望LZ说下结果,如果LZ成功了那就是我理解错了,这样我也可以顺便学习一下。当然把编译好的exe发上来更好,我可以拿去反汇编学习下masm的类型转换机制。
2011-2-16 14:27
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
masm32里带的

; #########################################################################

    ; -----------------------------------------
    ; This procedure was written by Tim Roberts
    ; -----------------------------------------

	.486
	.model	flat
   option casemap :none  ; case sensitive


; Convert an 8-byte double-precision value to an ASCII string.  
;
; If the value is an integer of less than 16 digits, we convert it to
; an integral value.
;
; If the value is between 0.1 and 9999999.0, we convert it to a decimal
; value.
;
; Otherwise, we convert it to a scientific notation with 1 digit
; left and up to 6 digits right of the decimal, with a 3 digit exponent:
;    9.999999E+999
;
; Note that these rules differ somewhat from the '%g' specifier in printf.  
; But, since you have the source code, you can make this do whatever you 
; want.
;
; I've tried to include comments on how to convert this to use 10-byte
; doubles.
;
; Tim N. Roberts.


; These are bits in the FP status word.

FP_LESSTHAN	equ	01h
FP_EQUALTO	equ	40h

	.data

; I'd rather this was local to the procedure, but masm doesn't do 
; local arrays correctly.

szTemp	db	18 dup (0)

	.code

PowerOf10	proto

ten	dq	10.0
ten16	dq	1.0e16
rounder	dq	5.0e10


; Convert a floating point register to ASCII.  For internal use.
; The result always has exactly 18 digits, with zero padding on the
; left if required.
;
; Entry:	ST(0) = a number to convert, 0 <= ST(0) < 1E19.
;		szTemp = an 18-character buffer.
;
; Exit:		szTemp = the converted result.

FloatToBCD2	PROC	public uses esi edi

    sub esp, 10

	; The fbstp instruction converts the top of the stack to a
	; packed BCD form in ten bytes, with two digits per byte.  The top 
	; byte has the sign, which we ignore.

    fbstp [esp]

	; Now we need to unpack the BCD to ASCII.

    lea esi, [esp+8]
    lea edi, [szTemp]
    mov ecx, 9

    .REPEAT
	mov al, [esi]		; xxxx xxxx AAAA BBBB
	dec esi
	rol ax, 12		; BBBB xxxx xxxx AAAA
	rol ah, 4		; xxxx BBBB xxxx AAAA
	and ax, 0f0fh		; 0000 BBBB 0000 AAAA
	add ax, 3030h		; 3B3A
	mov [edi], ax
	add edi, 2
	dec ecx
    .UNTIL ZERO?

    add esp, 10
    ret

FloatToBCD2	ENDP

;
; Convert a double precision number to a string.
;
; Entry:	fpin = 8-byte double to convert
;		szDbl = character buffer
;
; Exit:		szDbl = converted value
;
; szDbl should be at least 19 bytes long.
;		

FloatToStr2	PROC	stdcall public USES esi edi, 
		fpin: QWORD, 
		szDbl: PTR CHAR

    LOCAL	iExp: DWORD
    LOCAL	stat: WORD
    LOCAL	mystat: WORD

; Special case zero.  fxtract fails for zero.
	
    mov edi, [szDbl]

    .if	(dword ptr [fpin] == 0) && (dword ptr [fpin][4] == 0)
      mov byte ptr [edi], '0'
      mov byte ptr [edi][1], 0
      ret
    .endif

; Check for a negative number.

    .if	(sdword ptr [fpin][4] < 0)
      and byte ptr [fpin][7], 07fh	; change to positive
      mov byte ptr [edi], '-'		; store a minus sign
      inc edi
    .endif

; Initialize the floating point unit and load our value onto the stack.

    fclex
    fstcw [stat]
    mov [mystat], 027fh
    fldcw [mystat]

    fld [fpin]
    fld st(0)

; Compute the closest power of 10 below the number.  We can't get an
; exact value because of rounding.  We could get close by adding in
; log10(mantissa), but it still wouldn't be exact.  Since we'll have to
; check the result anyway, it's silly to waste cycles worrying about
; the mantissa.
;
; The exponent is basically log2(fpin).  Those of you who remember
; algebra realize that log2(fpin) x log10(2) = log10(fpin), which is
; what we want.

    fxtract			; ST=> mantissa, exponent, fpin
    fstp st(0)			; drop the mantissa
    fldlg2			; push log10(2)
    fmulp st(1), st		; ST = log10(fpin), fpin
    fistp [iExp]		; ST = fpin

; An 8-byte double can carry almost 16 digits of precision.  Actually, it's
; 15.9 digits, so some numbers close to 1E17 will be wrong in the bottom
; digit.  If this is a concern, change the '16' to a '15'.
;
; A 10-byte double can carry almost 19 digits, but fbstp only stores the
; guaranteed 18.  If you're doing 10-byte doubles, change the '16' to '18'.

    .IF	([iExp] < 16)
      fld st(0)			; ST = fpin, fpin
      frndint			; ST = int(fpin), fpin
      fcomp st(1)		; ST = fpin, status set
      fstsw ax
      .IF (ah & FP_EQUALTO)	; if EQUAL

; We have an integer!  Lucky day.  Go convert it into a temp buffer.

	call FloatToBCD2

	mov eax, 17
	mov ecx, [iExp]
	sub eax, ecx
	inc ecx
	lea esi, [szTemp][eax]

; The off-by-one order of magnitude problem below can hit us here.  
; We just trim off the possible leading zero.

	.IF (byte ptr [esi] == '0')
	  inc esi
	  dec ecx
	.ENDIF

; Copy the rest of the converted BCD value to our buffer.

	rep movsb
	jmp ftsExit

      .ENDIF
    .ENDIF

; Have fbstp round to 17 places.

    mov eax, 16; experiment
    sub eax, [iExp]	; adjust exponent to 17
    call PowerOf10

; Either we have exactly 17 digits, or we have exactly 16 digits.  We can
; detect that condition and adjust now.

    fcom [ten16]
    ; x0xxxx00 means top of stack > ten16
    ; x0xxxx01 means top of stack < ten16
    ; x1xxxx00 means top of stack = ten16
    fstsw ax
    .IF (ah & 1)
      fmul [ten]
      dec iExp
    .ENDIF

; Go convert to BCD.

    call FloatToBCD2

    lea esi, [szTemp+1]		; point to converted buffer

; If the exponent is between -15 and 16, we should express this as a number
; without scientific notation.

    mov ecx, iExp
    .IF (SDWORD PTR ecx >= -15) && (SDWORD PTR ecx <= 16)

; If the exponent is less than zero, we insert '0.', then -ecx
; leading zeros, then 16 digits of mantissa.  If the exponent is
; positive, we copy ecx+1 digits, then a decimal point (maybe), then 
; the remaining 16-ecx digits.

      inc ecx
      .IF (SDWORD PTR ecx <= 0)
        mov word ptr [edi], '.0'
	add edi, 2
	neg ecx
	mov al, '0'
	rep stosb
	mov ecx, 16
      .ELSE
        rep movsb
        mov byte ptr [edi], '.'
        inc edi
        mov ecx, 16
        sub ecx, [iExp]
      .ENDIF
      rep movsb

; Trim off trailing zeros.

      .WHILE (byte ptr [edi-1] == '0')
	dec edi
      .ENDW

; If we cleared out all the decimal digits, kill the decimal point, too.

      .IF (byte ptr [edi-1] == '.')
	dec edi
      .ENDIF

; That's it.

      jmp ftsExit

    .ENDIF


; Now convert this to a standard, usable format.  If needed, a minus
; sign is already present in the outgoing buffer, and edi already points
; past it.

    movsb				; copy the first digit
    mov byte ptr [edi], '.'		; plop in a decimal point
    inc edi
    movsd				; copy four more digits
    movsw				; copy two more digits

if 0

; The printf %g specified trims off trailing zeros here.  I dislike
; this, so I've disabled it.  Comment out the if 0 and endif if you
; want this.

    .WHILE (byte ptr [edi][-1] == '0')
      dec edi
    .ENDW
endif

; Shove in the exponent.  If you support 10-byte reals, remember to
; allow 4 digits for the exponent.

    mov byte ptr [edi], 'e'	; start the exponent
    mov eax, [iExp]
    .IF (sdword ptr eax < 0)	; plop in the exponent sign
      mov byte ptr [edi][1], '-'
      neg eax
    .ELSE
      mov byte ptr [edi][1], '+'
    .ENDIF

    mov ecx, 10

    xor edx, edx
    div ecx
    add dl, '0'
    mov [edi][4], dl	; shove in the ones exponent digit

    xor edx, edx
    div ecx
    add dl, '0'
    mov [edi][3], dl	; shove in the tens exponent digit

    xor edx, edx
    div ecx
    add dl, '0'
    mov [edi][2], dl	; shove in the hundreds exponent digit

    add edi, 5		; point to terminator

; Clean up and go home.

ftsExit:
    mov byte ptr [edi], 0
    fldcw [stat]		; restore control word
    fwait

    ret

FloatToStr2	ENDP

	end
2011-2-16 19:30
0
雪    币: 25
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
测试过了。其实Mx¢Xgt的方法不行。不是我要的结果,
唯一的做法是。把dowrd 转成qword,再调用。FloatToStr2  就能得到字串
2011-2-17 22:36
0
雪    币: 45
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
tim robert写的代码,他说masm32不能正确的支持局部数组变量,难道local szTemp[18]:byte不能用么……
2011-2-17 22:49
0
雪    币: 247
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
用sprintf比较方便 在aogo汇编网站有个做好的汇编调用c语言函数包 楼主可以直接去下载调用
2011-2-18 09:33
0
雪    币: 359
活跃值: (41)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
sprintf最后一个参数是不定参数,这种参数类型在C/C++里面如果传入float(4字节浮点)是会被编译器自动转换为double(8字节浮点)的,其实前面那个"%f"和"%lf"在32位编译器下面根本没区别(看sprintf源代码可以知道)。既然sprintf是API,那么即使用汇编调用也应该遵守这一规则才能成功才对,所以我才觉得5L说的方法应该行不通,不过先把4字节浮点转换为8字节浮点再用他的方法是可以的。

************************************************

前面只说了一处疑点,还有一处疑点是我原本认为调用C/C++运行时库是需要初始化的,而2L那段代码调用sprintf,而sprintf就是C/C++运行时库的一部分,但是他没有初始化过,所以我甚至怀疑2L那段代码到底能不能工作。为了证实猜测,于是我用VC进行测试,指定自定义程序入口来跳过C/C++运行时库的初始化工作,结果我发现我的猜测错了,2L的代码可以正常工作,当然先把4字节浮点转换为8字节浮点再用2L的方法也可以正常工作。不过这并不是说C/C++运行时库不需要初始化,而是msvcrt.dll在DllMain里面会初始化,所以即使不手动初始化,实际上初始化还是完成了。
2011-2-18 10:25
0
游客
登录 | 注册 方可回帖
返回
//