Bug with log2, assembler guru help needed

Started by
6 comments, last by Florent 974 13 years, 4 months ago
Hi, I have a Log2 method which have inconsistent result between debug & release

here's the c++ code
	u32	Log2( float _f )	{		#ifdef XTM_MACOSX			return ilogbf( _f );		#else			const float b = 1.4426950f; // this is 1.0f / log( 2.0f );			float r = log( _f ) * b;			return ((u32)r);		#endif	}


Here's the debug asm
?Log2@xtm@@YAKM@Z PROC					; xtm::Log2; 28   : 	{	push	ebp	mov	ebp, esp	sub	esp, 24					; 00000018H	mov	eax, -858993460				; ccccccccH	mov	DWORD PTR [ebp-24], eax	mov	DWORD PTR [ebp-20], eax	mov	DWORD PTR [ebp-16], eax	mov	DWORD PTR [ebp-12], eax	mov	DWORD PTR [ebp-8], eax	mov	DWORD PTR [ebp-4], eax; 29   : 		#ifdef XTM_MACOSX; 30   : 			return ilogbf( _f );; 31   : 		#else; 32   : 			//TODO bug inconsistent calculation between release & debug ( IEEE Strict & fast floating point ? examine asm ); 33   : ; 34   : 			//return log( _f ) / log( 2.0f );; 35   : 			const float b = 1.4426950f; // 1.0f / log( 2.0f );	movss	xmm0, DWORD PTR __real@3fb8aa3b	movss	DWORD PTR _b$[ebp], xmm0; 36   : 			float r = log( _f ) * b;	push	ecx	movss	xmm0, DWORD PTR __f$[ebp]	movss	DWORD PTR [esp], xmm0	call	?log@@YAMM@Z				; log	add	esp, 4	fmul	DWORD PTR _b$[ebp]	fstp	DWORD PTR _r$[ebp]; 37   : 		//printf( "log(%f) = %f (%u)\n", _f, r, (u32)r );; 38   : 			return ((u32)r);	fld	DWORD PTR _r$[ebp]	fnstcw	WORD PTR tv74[ebp]	movzx	eax, WORD PTR tv74[ebp]	or	eax, 3072				; 00000c00H	mov	DWORD PTR tv77[ebp], eax	fldcw	WORD PTR tv77[ebp]	fistp	QWORD PTR tv79[ebp]	fldcw	WORD PTR tv74[ebp]	mov	eax, DWORD PTR tv79[ebp]; 39   : 		#endif; 40   : 	}	add	esp, 24					; 00000018H	cmp	ebp, esp	call	__RTC_CheckEsp	mov	esp, ebp	pop	ebp	ret	0


And the release asm:

?Log2@xtm@@YAKM@Z PROC					; xtm::Log2; 28   : 	{	push	ebp	mov	ebp, esp	sub	esp, 8; 29   : 		#ifdef XTM_MACOSX; 30   : 			return ilogbf( _f );; 31   : 		#else; 32   : 			//TODO bug inconsistent calculation between release & debug ( IEEE Strict & fast floating point ? examine asm ); 33   : ; 34   : 			//return log( _f ) / log( 2.0f );; 35   : 			const float b = 1.4426950f; // 1.0f / log( 2.0f );; 36   : 			float r = log( _f ) * b;; 37   : 		//printf( "log(%f) = %f (%u)\n", _f, r, (u32)r );; 38   : 			return ((u32)r);	fld	DWORD PTR __f$[ebp]	fldln2	fxch	ST(1)	fnstcw	WORD PTR tv78[ebp]	fyl2x	movzx	eax, WORD PTR tv78[ebp]	or	eax, 3072				; 00000c00H	mov	DWORD PTR tv80[ebp], eax	fmul	DWORD PTR __real@3fb8aa3b	fldcw	WORD PTR tv80[ebp]	fistp	QWORD PTR tv64[ebp]	mov	eax, DWORD PTR tv64[ebp]	fldcw	WORD PTR tv78[ebp]; 39   : 		#endif; 40   : 	}	mov	esp, ebp	pop	ebp	ret	0


In debug Log2( 1024.0f ) returns 9 and in release 10

I'm compiling under VS2010 with the following settings:

Debug Release
Optim Disabled Full
Enable Intrinsic Functions No Yes
Enable Ehanced Instr. set SSE2 SSE2
Floating point model Fast Fast
Floating Point Exceptions No No

Can you see what's wrong here ? I would greatly appreciate any help with this issue
Advertisement
It seems your release build actually calculates log2 with an ASM instruction, then converts that to the natural logarithm, and then multiplies with your b to convert back to log2.
I guess that floating point inaccuracy leads to something like 9.999f which is rounded down when converted to an integer.

(Edited)
So I'll try adding 0.5f to my result before casting it back to int, that should do the trick...

Thanks !
Are you only interested in return the integer part (floor:ing away the decimals) of a log2 operation of a 32-bit floating point value?
If that is the case, using bit manipulations on the float representation is easier, since the exponent is directly stored in the float itself.

Here is some pseudocode that should work with 32-bit float, including a little bit of error checking
in = (int)_fIf in&&0x80000000 == 0x80000000  //Return NaN if input is negative	return 0x7f800001	//Return NaNelse if (in&&0x7f800000 == 0x7f800000) &&(in&&0x007fffff != 0)	//Return NaN if input NaN	return 0x7f800001  //NaNelse if in == 0x7f800000	return 0x7f800000else	return in>>23  //Return exponent of 32 bit float


http://en.wikipedia.org/wiki/Single_precision_floating-point_format
(It's the 8 green bits in the figure you are after)


Awesome ! Thanks !
What I needed was a fast way to get the number of mipmaps for a given texture size ( for a 1024x1024 that is 11 mip levels) historicaly I also used this function for level of detail on a procedural terrain generator it is why the input is a float.

But I think I'll make a separate version with integer math only. I believe there is an asm instruction that tell you the "position" of the most significant bit that is set to 1.

Eg binary value 00010000 (16 in base 10) would result in 4

Do you know a (portable if possible) way to do this ?
I could shift the value one bit at a time in a loop. But I'm trying to avoid loops for that purpose.
Check under Finding integer log base 2 of an integer (aka the position of the highest bit set) here: http://www-graphics.stanford.edu/~seander/bithacks.html.
From: http://stackoverflow.com/questions/994593/how-to-do-an-integer-log2-in-c

#include <stdint.h>static inline uint32_t log2(const uint32_t x) {  uint32_t y;  asm ( "\tbsr %1, %0\n"      : "=r"(y)      : "r" (x)  );  return y;}
Thanks a thousand times for your help ! :)

By the way, just to introduce myself and my work, I'm an "ex professional game developer" (although not very sharp in the asm field) I retired to work on my own project which is a battlefield like game, but oriented toward simulation ( eg plane physics use a flight model similar to X-plane )

The engine is cross platform :
- iPhone iPad
- Linux
- Windows ( GL & DX9 through DX11)
- MacOSX


The terrain is a hires (1meter resolution) satellite data of Reunion Island ( which is very mountainous)

Well ... See you soon in the IOTD !

[Edited by - Florent 974 on December 4, 2010 7:10:01 AM]

This topic is closed to new replies.

Advertisement